Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does ASP.NET Web Api model binding uses the parameter type to determine the source of the value?

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.

like image 778
Maarten Kieft Avatar asked Nov 15 '13 16:11

Maarten Kieft


People also ask

What is parameter binding in ASP.NET Web API?

Binding is a process to set values for the parameters when Web API calls a controller action method. In this article, we learn how to map Web API methods with the different types of the parameters and how to customize the binding process. Web API tries to get the value from the URI.

What is model binding ASP.NET and why is it important?

ASP.NET MVC model binding allows mapping HTTP request data with a model. It is the procedure of creating . NET objects using the data sent by the browser in an HTTP request. Model binding is a well-designed bridge between the HTTP request and the C# action methods.


2 Answers

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.

like image 167
David L Avatar answered Oct 23 '22 04:10

David L


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
}
like image 38
Trevor Elliott Avatar answered Oct 23 '22 04:10

Trevor Elliott