I try to make GET request via WebApi with complex object.
Request is like this:
[HttpGet("{param1}/{param2}")]
public async Task<IActionResult> GetRequest(string param1, int param2, [FromBody] CustomObject[] obj)
{
throw new NotImplementException();
}
Where CustomObject is:
[DataContract]
public class CustomeObject
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Email { get; set; }
}
How do I compose a valid GET request?
[FromBody] CustomObject[] obj ... GET request has no message body and thus you should change it to FromUri.
Sure, take a look at Documentation
public class GeoPoint
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public ValuesController : ApiController
{
public HttpResponseMessage Get([FromUri] GeoPoint location) { ... }
}
Request would be like below, essentially you pass the entire object data as query string
http://localhost/api/values/?Latitude=47.678558&Longitude=-122.130989
An array of object example can be found in another post pass array of an object to webapi
If your complex object is defined by the server, you can model bind to it through the URI and dot notate the properties in the routing template. My advice is to keep this model to one level of properties. You can bind to more complex objects, but you'll quickly find yourself having to write your own model binder.
Note that your argument decorator will need to be changed to [FromUri] to bind a complex object through the Uri. Servers are not required to support GET bodies and most do not.
public class CustomObject
{
public string Name { get; set; }
public string Email { get; set; }
}
[HttpGet]
[Route("{foo.Name}/{foo.Email}")]
public HttpResponseMessage Get([FromUri]CustomObject foo)
{
//...body
return Request.CreateResponse(HttpStatus.OK, foo);
}
You can pass it as a stringified json or use the request body via post instead of get.
Related
I am building an API to generate Invoice Number.
I have the following InvItem class
public class InvItem
{
public string ItemCode;
public double Quantity;
public double SaleValue;
}
I have the following Controller method
[Route("api/InvoiceMaster/GetInvoiceNum")]
[HttpGet]
public IActionResult GetInvoiceNum(
string xDateTime,
string BuyerName,
double TotalBillAmount,
InvItem[] items
)
{
...
var invItems = new List<InvItem>();
invItems.AddRange(items);
...
return Ok();
}
Invoice can have one or more items. Now I want to call that method from postman (or any other application) using GET request.
I have already built that method using POST request and reading parameters from request body. But the requirement here is STRICTLY Get Request.
I have tried the following url but cannot get the value of items in controller's action method 'GetInvoiceNum'
https://localhost:44365/api/InvoiceMaster/GetInvoiceNum?xDateTime=2020-01-01 12:00:00&BuyerName=elon&TotalBillAmount=1519&items[0].ItemCode=001897&items[0].Quantity=1&items[0].SaleValue=19&items[1].ItemCode=002899&items[1].Quantity=1&items[1].SaleValue=1500
How can I pass this array of objects to api?
You have to add from [FromQuery]
public IActionResult GetInvoiceNum(
[FromQuery] string xDateTime,
[FromQuery] string BuyerName,
[FromQuery] double TotalBillAmount,
[FromQuery] InvItem[] items
)
and convert fields to properties by adding getters/setters
public class InvItem
{
public string ItemCode { get; set; }
public double Quantity { get; set; }
public double SaleValue { get; set; }
}
I have the following C# ASP.Net Core Web API controller method for creating an "entity" using a POST:
[HttpPost("example")]
[SwaggerResponse(200,"Ok")]
public async Task<IActionResult> Create([FromForm]MyModel create)
{
return Ok();
}
The model is defined as this:
public class MyModel
{
public string PropA { get; set; }
public string PropB { get; set; }
public List<OtherProp> Other { get; set; }
}
public class OtherProp
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
When this is shown in swagger, you can see the "Other" property array looks like this:
Instead of the broken down model. How do I get swagger to breakdown this model?
Your are missing the Swagger Decorator Attributes, follow this below and replace the attributes with your specific Types / MyModel
Since you didn't put up your actual code, to see how it works, use the default samples for e.g. you can Install my Swashbuckle.Examples NuGet package. Decorate your methods with the new SwaggerResponseExample attributes below and you will see it work just fine!
// These attributes will help with your nested objects
[SwaggerResponse(HttpStatusCode.OK, Type=typeof(IEnumerable<Country>))]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(CountryExamples))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(IEnumerable<ErrorResource>))]
public async Task<HttpResponseMessage> Get(string lang)
Also ensure you have it configured like so
configuration
.EnableSwagger(c =>
{
c.OperationFilter<ExamplesOperationFilter>();
})
.EnableSwaggerUi();
I have this function in jQuery
var uri = "api/queries";
function test(){
var params = {
origin: $('#depair').val(),
destination: $('#destair').val(),
departure_date: $('#depdate').val(),
currency: $('#currency').val(),
}
$.getJSON(uri, params)
.done(function (data) {
console.log(data);
});
}
Which sends the request to this Controller:
public class QueriesController : ApiController
{
[HttpGet]
public string GetInfo()
{
return "blah";
}
}
So, the request looks like this
http://localhost:55934/api/queries?origin=&destination=&departure_date=¤cy=
How do I access the parameters of the request from inside the controller GetInfo method?
You can include them as parameters to your function.
[HttpGet]
public string GetInfo(string origin, string destination, string departure_date, string currency)
{
return "blah";
}
You can use Model Binding. First create a class (ViewModel) like this:
public class Querie
{
public string Origin { get; set; }
public string Destination { get; set; }
public string Departure_date { get; set; }
public string Currency { get; set; }
}
Then include this class as parameter to your method like this:
public class QueriesController : ApiController
{
[HttpGet]
public string GetInfo(Querie querie)
{
//querie.Origin
return "blah";
}
}
Model binding maps data from HTTP requests to action method parameters. The parameters may be simple types such as strings, integers, or floats, or they may be complex types. This is a great feature of MVC because mapping incoming data to a counterpart is an often repeated scenario, regardless of size or complexity of the data.
var origin = Request.QueryString["origin"];
Replacing "origin" with your parameter.
I've got a list of objects in JSON that isn't recognized by a WebApi2 controller
The JSON list is the following:
{
"FirstObjectType": [{"Name": "the_name"}],
"SecondObjectType": [{"Label": "01_obj"}, {"Label": "02_obj"}]
}
The Model class is:
public class CompositeObject
{
[JsonProperty("FirstObjectType")]
public List<FirstObject> fo { get; set; }
[JsonProperty("SecondObjectType")]
public List<SecondObject> so { get; set; }
}
The controller is:
public IHttpActionResult PostList([FromBody] CompositeObject jsonList)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
List<FirstObject> fo_list = jsonList.fo;
foreach (var item in fo_list)
{
db.FirstObject.Add(item);
db.SaveChanges();
}
return StatusCode(HttpStatusCode.OK);
}
When I submit the Post action, the controller recognize both lists in CompositeObject jsonList as Null
There is a problem in your model, where names are not being matched. You have to update model as:
public class FirstObjectType
{
public string Name { get; set; }
}
public class SecondObjectType
{
public string Label { get; set; }
}
public class RootObject
{
public List<FirstObjectType> FirstObjectType { get; set; }
public List<SecondObjectType> SecondObjectType { get; set; }
}
I have assumed that FirstObjectType contains string with name Name and SecondObjectType contains string with name Label. Make sure to use same names for properties of FirstObjectType and SecondObjectType class as in JSON string.
The issue was in the client code because I missed to set the Content-type as application/json in the header section.
In this way the WebApi server doesn't recognize in the right way the JSON object (I think that the server look for a x-www-form-urlencoded type)
So, the code above is right, but I have found another solution
In the WebApi controller:
using Newtonsoft.Json.Linq;
public IHttpActionResult PostList([FromBody] JObject ReceivedObjectsList)
{
var receivedLists = ReceivedObjectsList.Properties();
List<FirstObject> fo = ReceivedObjectsList["FirstObjectType"].ToObject<List<FirstObject>>();
List<SecondObject> so = ReceivedObjectsList["SecondObjectType"].ToObject<List<SecondObject>>();
...
}
I'm trying to pass a JSON array to an ApiController but the string values aren't deserializing (they are set to null values). The strange thing is that I still get the correct number of elements.
A have an ApiController:
[RoutePrefix("api/language")]
public class LanguagePairApiController : ApiController
With a post method:
// POST: api/language/create
[HttpPost]
[Route("create")]
public string Create([FromBody]LanguagePair[] languagePairs)
I'm sending JSON to it:
[
{"Key":"Test","Value":"Test","Version":"1.0"},
{"Key":"Areyousure","Value":"Are you sure?","Version":"1.0"},
{"Key":"File","Value":"File","Version":"1.0"}
]
And this is the class I'm trying to map it to:
public class LanguagePair
{
public string Key { get; set; }
public string Value { get; set; }
public string Version { get; set; }
}
But the string values are coming through as null:
What am I missing?
EDIT: I've figured out one answer to this and posted it below. But I'm still looking for a better answer...
I figured it out. I needed to decorate the class with DataContract and DataMember attributes:
{
[DataContract]
public class LanguagePair
{
[DataMember]
public string Key { get; set; }
[DataMember]
public string Value { get; set; }
[DataMember]
public string Version { get; set; }
}
}
Read Parameter Binding in ASP.NET Web API
You need to remove [FromBody] attribute from your action...
// POST: api/language/create
[HttpPost]
[Route("create")]
public string Create(LanguagePair[] languagePairs) { ... }
and you can keep your class lean as you originally had it:
public class LanguagePair
{
public string Key { get; set; }
public string Value { get; set; }
public string Version { get; set; }
}
Using [FromBody]
To force Web API to read a simple type from the request body, add the
[FromBody] attribute to the parameter:
public HttpResponseMessage Post([FromBody] string name) { ... }
In this example, Web API will use a media-type formatter to read the
value of name from the request body. Here is an example client
request.
POST http://localhost:5076/api/values HTTP/1.1
User-Agent: Fiddler
Host: localhost:5076
Content-Type: application/json
Content-Length: 7
"Alice"
When a parameter has [FromBody], Web API uses the Content-Type
header to select a formatter. In this example, the content type is
"application/json" and the request body is a raw JSON string (not a
JSON object).