Why do I have to specify [FromUri] to get this to work?
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
chobo2
Updated on June 06, 2022Comments
-
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 aPost
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 almost 11 yearsSo 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 almost 11 yearsMaybe 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 almost 11 yearsSo 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 almost 11 yearsYes, 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 forGet(myobject)
andPost(myobject)
... but in case ofGet
, we will inform the API that it should be build from the data sent as URI parts -
chobo2 almost 11 yearshmm. 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 almost 11 yearsWell, 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 almost 11 yearsWell 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 almost 11 yearsParamters 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 almost 11 yearsIf I use a simple type for Post or Put won't they be pass by uri then?
-
Radim Köhler almost 11 yearsNo. 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)