Why do I have to specify [FromUri] to get this to work?

18,369

The Web API paramter binding (extract from here:Routing and Action Selection) does this:

Parameter Bindings. A parameter binding is how Web API creates a value for a parameter. Here is the default rule for parameter binding:

  • Simple types are taken from the URI.
  • Complex types are taken from the request body.

So, any complex type (like the class StoreDm is) is by default expected to be part of the body

As stated in the standard defintion Hypertext Transfer Protocol 4.3 Message Body

The rules for when a message-body is allowed in a message differ for requests and responses.

The presence of a message-body in a request is signaled by the inclusion of a Content-Length or Transfer-Encoding header field in the request's message-headers. A message-body MUST NOT be included in a request if the specification of the request method (section 5.1.1) does not allow sending an entity-body in requests.

So, while Web API provides some common functionality, it tries to be general. There could be requests with or without message-body. So, having this, the action selection and param binding is common, without inferring of the "current" request specific, and maybe obvious settings (we would think, that GET would always have all parameters (properties of StoreDm object) in the URI... but the engine does not)

The POST would bind the StoreDm out of the box, because its properties will be found in the body, standard binding for complex objects.

The GET breaks the rules, the properties (of Complex type) are in the URI, so we only have to inform the framework: [FromUri]. In other cases, Method will be found, null (bounded from message-body) will be passed

Share:
18,369
chobo2
Author by

chobo2

Updated on June 06, 2022

Comments

  • chobo2
    chobo2 almost 2 years

    I am using Asp.NET WebApi and one thing that confuses me is the binding when a request is done.

    I have this ViewModel:

    [DataContract(Name="Store")]
    public class StoreDm
    {
        [DataMember(IsRequired = true)]
        [MinLength(3)]
        public string Name { get; set; }
    
        [DataMember(IsRequired = true)]
        public double Latitude { get; set; }
    
        [DataMember(IsRequired = true)]
        public double Longitude { get; set; }
    }
    
    public HttpResponseMessage GetStoreNames(StoreDm vm)
    {
        if (ModelState.IsValid)
        {
    
        }
    }
    
    RestClient c = new RestClient("http://localhost:3333/api/store");
    RestRequest r = new RestRequest("/GetStoreNames", Method.GET);
    r.AddParameter("Name", autoComplete);
    r.AddParameter("Latitude", "4");
    r.AddParameter("Longitude", "-7");
    var d =  c.BuildUri(r);
    c.ExecuteAsync(r, response2 =>
      {
          var content = response2.Content;
      });
    

    My StoreDm is Null.

    I don't get this on so many levels. First I setup IsRequired on all my properties yet for whatever reason the ModelState thinks "null" ViewModel is valid.

    Second I don't get why it is null. I have to add [FromUri] to get it to bind. What happens if this would have been a Post and have the same restClient code but also someone is using fiddler body request.

    If I am forced to put [FromUri] then I don't think the fiddler body request will work.

    How could I have it so both requests go through and bind properly?

  • chobo2
    chobo2 almost 11 years
    So for all "Complex Gets" always needs to have [FromUri] in there because of design it assumes that it may not have a message-body? Still kinda unclear why Get is so special that they could not make it work like POST. What happens if someone passes GET through the body. That is kinda lame that if you pass in a body for the GET it won't be bound.
  • Radim Köhler
    Radim Köhler almost 11 years
    Maybe I explained that incorrectly. The POST and GET are the same. But in case of the POST, the object/ parameter of the Post method is automatically build from the body. While in case of the Get, the data could be passed only via URL. So the way how we send data are different. That's why we have to "explain" to API [FromUri] in case of get
  • chobo2
    chobo2 almost 11 years
    So if I would pass to the Post build by URI then it would not get bound? Get is automatically built from what then nothing?
  • Radim Köhler
    Radim Köhler almost 11 years
    Yes, if we'll pass anything in the URI, then it won't be bound, unless it is a SimpleType OR it is marked as [FromUri]. The mechanism is really the same. Different is just the way the data is passed. And because it is totally different, we have to inform the API. So we can have simliar code for Get(myobject) and Post(myobject)... but in case of Get, we will inform the API that it should be build from the data sent as URI parts
  • chobo2
    chobo2 almost 11 years
    hmm. If I would use [FromBody] for GET does that bring back the advantage of bring it to be like POST? I guess in my API documentation for GET requests I have to specify that a URI needs to be built? For Post if they sent a URI instead of BODY would it bind? Now thinking about it I also have to always check for NULL when doing complex objects as it does not seem Model State gets updated in that scenario.
  • Radim Köhler
    Radim Köhler almost 11 years
    Well, I am not sure what you are trying to do. Mostly the Get is for give (server) me result (e.g. JSON) in the response-body. Post, Put is about that I am sending (to server) the changed/new data in the request-body. The URI in both cases should server for information: which data (e.g. ID)
  • chobo2
    chobo2 almost 11 years
    Well as you can see I have parameters in my GET and wondering how the best way to pass those parameters to the server is.
  • Radim Köhler
    Radim Köhler almost 11 years
    Paramters should be passed to server ONLY via PUT or POST if they should be persisted. If they are "part of a query", then they must go into the URL. So, I do not see any similarity in the Get(myObject), Post(myobject)... so I am not able to understand where the problem is ;)
  • chobo2
    chobo2 almost 11 years
    If I use a simple type for Post or Put won't they be pass by uri then?
  • Radim Köhler
    Radim Köhler almost 11 years
    No. principle (as I tried to explain) is simple. Simple types from URI, other from body. Method type does not matter. but because we can need different binding, we can use [FromUri] and also [FromBody]. With such parameter decoration, even the body simple types will be used (but it is a waste, while the body should contain different stuff, e.g. new object, updated object)