Assuming a ApiController with an action having multiple arguments:
[HttpPost]
public void AddAddress( Person person, Address address, int simple )
Or something like that.
Now I try to send a post request with content type application/json and json like this:
{
person: {...}, address: {..}, simple: 1
}
just assume it's valid json ;)
or in a json array like this:
[person, address, simple]
But WebApi won't recognize this and tell me it can't find the action, as it doesn't support multiple parameters in a json request.
I'm doing this from a C# client using HttpClient and we'd like to do post requests using json. I wanted to use the WebApi.Client package, which provides several useful extensions to the HttpClient, but we have a portable library project(which webapi client doesn't support).
How would I go about supporting this feature?
I tried to use an custom ActionFilter, which never get's to run as it can't find the action beforehand. I tried MediaTypeFormatter which doesn't know the context and the arguments, and also I tried ModelBinder, which also seems only on a per argument basis and doesn't always get executed.
Thanks!
The built-in parameter binding strategy in ASP.NET Web API with formatters only supports a single complex argument mapped to the request body. In your case, you are trying to map at least two complex arguments. The simplest solution without extending this infrastructure would be to create a new model class containing those arguments as properties
public class AddressModel
{
public Person Person { get; set; }
public Address Address { get; set; }
public int Simple { get; set; }
}
And use that model in your controller action
public void AddAddress( AddressModel model );
Related
I have a C# Web API, which has a method on a controller that takes a custom object. When calling this endpoint from my react application, I'm getting a 400 response from the API, with the error message below.
The categoryConfiguration field is required.
I understand that the API is failing to deserialize the request payload, but I have no idea why.
[HttpPost]
public async Task<IActionResult> CreateConfiguration(CategoryConfiguration categoryConfiguration)
{
return Ok(); // Never hits this breakpoint
}
I suspect it may have something to do with the custom object inheriting from an abstract class, although I have read similar posts indicating that it should work.
public class CategoryConfiguration : SystemConfiguration
{
public Guid CategoryId { get; set; }
public Category? Category { get; set; }
}
public abstract class SystemConfiguration
{
// Loads of properties.
}
The correct handling would be for the method to be executed, but currently, it doesn't actually reach the breakpoint in the controller.
This API has many other controllers which all work, in the same way, the only difference is the use of the abstract class. I can see that the object is being sent up through the browser.
I have tried using the from [FromBody] attribute which had no effect.
Any help would be appreciated.
Thanks.
I found out the issue was down to some backend validation rules (Fluent validation), basically, the timeout field was required to be greater than one of the nullable fields which caused the error code of
'Timeout must be greater than or equal to ''.
This resulted in a failed validation and rejected the request. I was able to find these error messages using Postman.
I have a method as described below which get user as parameter.
To send the user parameter values, I am using postman request/response tool.
My question is, if the request already have user parameter in body request (see at the postman print screen ) why do I need to directly specify [FromBody] in action controller? When this attribute removed, parameter send with null values.
Thanks
[HttpPost("register")]
public IActionResult Register([FromBody]User user)
{
//.. code here
}
public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
The [FromBody] directive tells the Register action to look for the User parameter in the Body of the request, rather than somewhere else, like from the URL. So, removing that confuses your method, and that's why you see null values as it's not sure where to look for the User parameters.
See: https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api for more information/learning.
For Model Binding in ASP.NET Core, MVC and Web APi uses the same model binding pattern.
There are default model bindings like Form Values, Route values and Query Strings. If these bindings fails, it will not throw an error, and return null.
If you do not want to add [FromBody], you could try Form Values binding and send request from Postman like below:
[FromBody] will override default data source and specify the model binder's data source from the request body.
You could refer Model Binding in ASP.NET Core for detail information
the attribute [FromBody] ensures the API reads the JSON payload in the postman body. For more info, read
https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
I would like to write method validation processes which will be similar to data annontations presented in Web API.
In web api we can validate an object, for example:
public class Numbers
{
[NumberOne]
public string Number1 { get; set; }
[NumberTwo]
public string Number2 { get; set; }
}
and as long as we define the attributes NumberOneAttribute and NumberTwoAttribute its gonna be ok.
The difference is that web api has access to the GlobalConfiguration.Configuration.Filters which it seems like signalr doesn't.
Is there anyway to validate requests by attributes? or I need to follow the worst case, validate each input in the invoked method?
Thanks,
Ori.
In SignalR 2.2.x there is no native way of achieving this, but there is a project on GitHub that that adds a Validation Module in the SignalR pipeline.
Basically, in order to use it, you add a new module to the pipeline:
GlobalHost.HubPipeline.AddModule(new ValidationModule());
Then, you can use attributes like [Required] for the models' properties and then decorate the desired methods with the [Validate] attribute.
Note that this is a proof of contept project.
Best regards!
Since a few days I'm trying to create my own web api controller. Duo to the rest conventions I need to use a post request to create an object. To get concrete, Im having this controller with this action:
public class ReservationController : ApiController
{
[HttpPost]
public void Create(int roomId, DateTime arrivalDate)
{
//do something with both parameters
}
}
This code is not working when I fire a post request at it, I'm receiving a 404 exception something like this:
No action was found on the controller 'Some' that matches the request.
The reason for it is that simple types are read from the query string, complex types from the body, according to this aricle. The web api uses the parameters to match the action to a request and can't therefore map my action to the request.
I do know that I can use the [frombody] tag, but you can only apply that to one parameter and I have 2. I also know that I can create a wrapper object which have both the parameters, but I'm not willing to use wrappers for all my calls.
So I do know that I can work around this by these methods. I also think that this is caused by the fact that the body of the post request can only be read once. But my actual question is:
Why is the source of a parameter determined by it's type and not by it's availability, especially when the conventions state that you should make for example a post request for creation? In MVC this is the case, why isn't it in the web api?
Best regards,
BHD
FINAL UPDATE
Since I'm getting some upvotes, problably more people are facing the same question. In the end it comes to this: Web-Api != MVC. It's simply not the same thing and the web api team made different design decisions than the mvc team I guess.
It seems that you have a fundamental misunderstanding of how Web API actually works.
Web API routing is driven off of verbiage, not the method names. "SomeMethod" actually translates to zero useful information for Web API. As a result, if I post
api/some/some?id=1
OR
api/some/somemethod?id=1
OR EVEN
api/some/?id=1
and the SomeMethod endpoint is the ONLY available POST, it will hit that endpoint.
As such, first of all, make sure you have only one POST on that api controller. If you do, POSTing to it from any test client using either of the query strings above will work just fine.
You can use the [FromBody] attribute on the parameter to force it to read from the body of the HTTP POST instead of the Uri. This is opposed to the [FromUri] attribute which does the opposite.
[HttpPost]
public void SomeAction([FromBody] int id)
{
//do something with id
}
Are you sure you're actually putting the id in the body? It could also be a routing issue. If this still doesn't work then maybe you should use Fiddler and copy the RAW output of your HTTP message here.
If you're packing multiple values into the body such as with JSON then you should use a model which should automatically be deserialized to:
public class PostModel
{
public int ID { get; set; }
public int SomeOtherID { get; set; }
}
[HttpPost]
public void SomeAction(PostModel postModel)
{
//do something with postModel.ID and postModel.SomeOtherID
}
You can actually do this straight out of the box in WebAPI, at least in 2.2 (.Net version 4.5.2). Your controller is correct. Using your controller, if you call it with a HTTP POST like this (tested through Fiddler):
http://localhost:58397/api/Reservation?roomId=123&arrivalDate=2015-12-17
You'll get the correct values of roomId = 123 and arrivalDate = 17.12.2015.
I suspect there's something wrong in your call to the WebAPI. Maybe post that call if you're still not getting it to work.
Withing a Silverlight Application, how does one get the ID from the URL when using MVC given the URL in the standard MVC Format {controller}/{action}/{id}
Is there anything MVC specific or does one need to parse this URL 'manually'?
Well that's simple, either look in Request.Params["id"] or - and that's more ellegant - add a argument with the name "id" to you Controller method (case does not matter).
public void Index(string id) { }
Works even for more complex objects, the MVC does a good job in retrieving the information and matching it to the arguments of the controller method.
public class Data {
public string ID { get; set; }
}
public void Index(Data data) {
// data.id should be set
}
Silverlight runs on the client, therefore it cannot access the values on the server. However, the sever can pass the info to the client.