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.
Related
Framework used is .Net Core 3.0 but tested in 2.2 and got the same behavior.
I am using a class to automatically bind the body request properties and that works pretty well, even without having the [FromBody] attribute on them.
Now, I added a new property in this class that will match a property from the header and it works if I use it directly into the controller, like this:
public IActionResult Test(TestRequest request, [FromHeader(Name = "Authorization")] string token)
However, when I try to get the same result by adding the [FromHeader] attribute into the class property, it doesn't work.
Here is a sample code to illustrate the issue:
[ApiController]
[Route("api")]
public class TestController : ControllerBase
{
[HttpPost]
[Route("Test")]
public IActionResult Test(TestRequest request)
{
Console.WriteLine("request.UserId: " + request.UserId);
Console.WriteLine("request.Token: " + request.Token);
return Ok();
}
}
public class TestRequest
{
[FromBody]
public string UserId { get; set; }
[FromHeader(Name = "Authorization")]
public string Token { get; set; }
}
Did anybody ever face the same issue?
You need to configure SuppressInferBindingSourcesForParameters as true in ConfigureServices in Startup.cs like below :
services.AddMvc().ConfigureApiBehaviorOptions(options =>
{
options.SuppressInferBindingSourcesForParameters = true;
});
Action:
[HttpPost]
[Route("Test")]
public IActionResult Test(TestRequest request)
And call the api with your Authorization header(not shown below) and body string, for postman
Update:
Since you use [FromBody] on the string property,it accepts a string instead of json object.
If you still would like to pass json object as { "userId" : "123" }, you could warp the userId into a model,for example:
public class User
{
public string UserId { get; set; }
}
public class TestRequest
{
[FromBody]
public User User { get; set; }
[FromHeader(Name = "Authorization")]
public string Token { get; set; }
}
I have this controller and action method:
[ApiController]
[Route("api/[controller]")]
public class AppointmentController : ControllerBase
{
[Route("{provider}/AvailableSlots")]
[HttpGet]
public Task<AvailableSlotsResponse> GetAvailableSlots(Request<AvailableSlotsRequest> request)
{
return null;
}
}
Here's the model:
public class Request<T> where T : class
{
[FromRoute]
public string Provider { get; set; }
[FromQuery(Name = "")]
public T Model { get; set; }
}
public class AvailableSlotsRequest
{
//[FromQuery(Name = "Location")] //Would prefer not to have to use this
public string Location { get; set; }
}
I need to use Location as the query param name in the URL in order to hit the endpoint, as expected.
eg. http://localhost/api/Appointment/Company/AvailableSlots?Location=SYD
However, when I view the Swagger page, the parameter is called Model.Location which is confusing for consumers of my API:
I can use [FromQuery(Name = "Location")] to force Swagger to display Location, however this feels very redundant and duplicates the property name.
Here is my Swagger set up in ConfigureServices():
services.AddSwaggerDocument(document =>
{
document.PostProcess = d =>
{
d.Info.Version = Configuration["APIVersion"];
d.Info.Title = $"{Configuration["ApplicationName"]} {Configuration["DomainName"]} API";
};
});
How can I make Swagger display Location instead of Model.Location, without having to duplicate the word "Location" in the [FromQuery] attribute?
Add to the controller parameter the attribute [FromRoute]:
public Task<AvailableSlotsResponse> GetAvailableSlots([FromRoute]Request<AvailableSlotsRequest> request)
Remove the attribute FromQuery in the Model property and uncomment the attribute FromQuery from de Location Property.
Unfortunately I had to use [FromQuery(Name = "<PropertyName>")].
However I found a better way:
[ApiController]
[Route("api/[controller]")]
public class AppointmentController : ControllerBase
{
[Route("{provider}/AvailableSlots")]
[HttpGet]
public Task<AvailableSlotsResponse> GetAvailableSlots(AvailableSlotsRequest request)
{
return null;
}
}
public class Request
{
[FromRoute]
public string ProviderName { get; set; }
}
public class AvailableSlotsRequest : Request
{
[FromQuery]
public string Location { get; set; }
}
This also means the model can use any attribute, compared to my first attempt where the T Model was decorated with [FromQuery]
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.
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>>();
...
}
When I have this method in an MVC Controller
[HttpPost]
public async Task<ActionResult> MyMethod(int param1, string param2)
{
//....
}
I can send a Json object {param1:1, param2:"str"} it works just fine and parameters are resolved. However when I do this for a WebApi 2 it doesn't work. Because [FromBody] can only be used by 1 parameter according to following example on documentation.
At most one parameter is allowed to read from the message body
// Caution: Will not work!
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }
How can we obtain the same behavior of MVC controller from WebApi controller?
Edit: Creating corresponding classes and replacing parameters is not an option, because a messaging tool checks these methods for maintenance. Signatures should remain the same.
Try to compose one object from these values:
public class Foo
{
public int id {get;set;}
public int name {get;set;}
}
public HttpResponseMessage Post([FromBody] Foo foo)
{
//some stuff...
}
If signature should remain the same, you can try to specify params in url, like that: myurl?id=1&name=Tom still via POST verb.
You can try like this:
public HttpResponseMessage Post([FromBody]dynamic value)
{
int id= value.id.ToString();
string name = value.name.ToString();
}
And pass json like following
{
"id":"1",
"name":"abc"
}
If you have to pass multiple parameter please use class object:
public class PortalClass
{
public ApplicationModel applicationModel { get; set; }
public string user_id { get; set; }
public string id { get; set; }
public object pageCollection { get; set; }
}
public object GetApplication(PortalClass data)
{
JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, PreserveReferencesHandling = PreserveReferencesHandling.None };
var myObject=JsonConvert.DeserializeObject<PageCollection>(data.pageCollection.ToString(), settings)
return null;
}
Client Side:
var data = {
user_id: userId,
id: id
};
http.post(url, data).then(
function (response) {
}, function (err) {
callback.reject(err);
});