FormUrlEncoded value mapped to action parameters

Topics: ASP.NET Web API
Jun 14, 2012 at 9:52 PM

Hi,

This question was asked in StackOverflow and I thought I knew the answer but I realised it does not work as I expected.

So in ASP.NET Web API RC, if we have such an action:

public string Post([FromBody] string value) 
{
    return value;
}

I expect to be able to send this request and get back the value but I get "null":

POST /api/Values

Content-Type: application/x-ww-form-urlencoded

value=something

 

So what am I doing wrong?

Jun 14, 2012 at 10:11 PM

For the given action method, you should send the content without "value"

POST /api/Values

Content-Type: application/x-ww-form-urlencoded

=something

The preferred way is to always use DTOs as action parameters

class foo
{
  public int bar {get; set;}
}
public foo Post(foo obj) 
{
    return obj;
}

Now, request can look like

POST /api/Values

Content-Type: application/x-ww-form-urlencoded

bar=14

 

Jun 14, 2012 at 10:21 PM
Edited Jun 14, 2012 at 10:23 PM

Sorry, that does not make any sense. Sending empty name is an ugly hack and not sure if it is valid according to HTTP spec.

Of course, having a DTO is preferred but I am expecting this simple scenario to be supported.

Is there a technical limitation causing this feature to be implemented as such? Is this because it is a single parameter and it works if there are more than one parameter?

Jun 14, 2012 at 11:31 PM

It is valid according to the HTTP spec.

Pulling body data in Web API means the whole body. You cannot "partially" bind to pieces of the body. Bhavesh's workarounds are both the correct way of dealing with this requirement.

Jun 14, 2012 at 11:39 PM

Thanks Brad. I did have a look at the RFC but I could not find a rule regarding name.

So does it mean MVC style parameter mapping to formurlencoded name/value is not supported? With formurlencoded  such a bread and butter of the web, having this special case makes sense. At the end of the day, this behaviour is intuitive hence being asked often.

Jun 14, 2012 at 11:52 PM
This is really a question of body vs. non-body data. For bodies, Web API demands that you make exactly one object and that it represents the whole body, regardless of what format it's in (JSON, XML, Form, etc.). Non-body data, on the other hand, works exactly like MVC, so you can pull individual values from the route data or query string (as well as get model binding to manufacture complex objects from those values).
Jun 15, 2012 at 12:00 AM

Thanks Brad. Also thanks a lot bhaveshc for giving the initial right answer.

I suppose this divergence from MVC in body-only content needs to be explained a bit more to the community. Will try to do that in a post.

Cheers

Jun 15, 2012 at 9:25 PM

I've posted a topic about this on the asp.net site

http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-1

- Mike

Jun 25, 2012 at 2:16 PM
Edited Jun 25, 2012 at 2:17 PM

While I understand the reasoning behind this, I find the Web API binder behavior far less intuitive than the MVC 3 one and more exposing implementation details than it should. If i change from Post(int id) to Post(Foo foo) with class Foo { int Id } and vice versa, I don't think the client should be updated at all. It shouldn't matter whether id is bound to a model or a simple value. Sending an empty name to comply with the web service definition really feels awkward in my opinion.

Coordinator
Jun 25, 2012 at 8:59 PM

I think this is a fair argument. We should consider supporting id=blah for simple types. Do you want to open an issue tracker item for this?

Daniel Roth

Jun 26, 2012 at 11:36 AM

Wow, this is really confusing and i agree, far less intuitive than mvc3. I just want to do a post with an int as the parameter, and this suddenly stopped working with the rc, and i can't figure out how to work around this without making a lot of pointless DTOs. I could have it in the querystring, but i am unable to find how to do an ajax post with jquery.ajax without putting the data in the body.

Coordinator
Jun 26, 2012 at 6:07 PM

How are you doing the POST? Is this an HTML form post or are you using JQuery?

Daniel Roth

Jun 26, 2012 at 6:09 PM

jquery. i got it working by using [FromBody] on the parameter and sending the data as { "": val }, but who would have thought that was needed. without this thread i would have banged my head against this problem for hours and hours.

Aug 9, 2012 at 10:57 AM
Edited Aug 9, 2012 at 10:58 AM

Was just bitten by this with our new project. Agree that this is counter-intuitive at best, and in my view a step backwards. As far as I recall this seemed to work with MVC 4 beta; if this was indeed the case, I have to say it's a little surprising to see such a functional, breaking change introduced rather late in the cycle.

Not fully convinced also that it makes the binder faster; if we're all forced to use complex types anyway, whose parsing/binding has to walk over the same parameters (possibly caching as it goes?), this seems to defeat the purpose?

I guess there's no way to go back on this one with a config option?

Coordinator
Aug 9, 2012 at 5:21 PM

Check out Mike Stall’s MVC style parameter binder for Web API and see if that’s what you want. I believe it has also been added to WebAPIContrib (or it’s in the process of being contributed).

Daniel Roth

From: davidcie [email removed]
Sent: Thursday, August 9, 2012 3:58 AM
To: Daniel Roth
Subject: Re: FormUrlEncoded value mapped to action parameters [ASPNETWebStack:359687]

From: davidcie

Was just bitten by this with our new project. Agree that this is counter-intuitive at best, and in my view a step backwards. As far as I recall this seemed to work with MVC 4 beta; if this was indeed the case, I have to say it's a little surprising to see such a functional, breaking change introduced rather late in the cycle.

Not fully convinced also that it makes the binder faster; if we're all forced to use complex types anyway, whose parsing/binding has to walk over the same parameters anyway (possibly caching them?), this seems to defeat the purpose?

I guess there's no way to go back on this one with a config option?

Aug 10, 2012 at 8:28 AM

Daniel, thank you ever so much for sharing that. I have to admit I lost all hope yesterday when I looked at the blog posts describing the New Way, and forgot all about the pluggability of model binders. It's great that, while the MVC team has its point of view, it also provides a way out in case someone feels different about it.

So again: thank you, I will check those out while bearing in mind potential performance issues in case we scale to Twitter size :-)

Regards,
Dawid Ciecierski

Sep 11, 2012 at 10:49 PM

Attempt to upgrade an older web-api project to version 4 and got stuck using fiddler ( I was up an running a web-api in about 2 week learng the older stuff), I this version its not as clear as the previous version. I was betting everything on this technology, not now. Life just got a bit too difficult. Attributes ([fromBody], [FromUri]) are good, but i believe not in this case. Not intuitive. Web api was almost perfect

 

 

Regards

 

Paul

Coordinator
Sep 12, 2012 at 5:40 PM

Hi Paul,

ASP.NET Web API has evolved substantially. Can you tell us what issues you ran into? Your feedback helps us continue to improve the framework.

Thanks,

Daniel Roth

Sep 13, 2012 at 10:47 AM

Thanks for taking the time to respond.

My main issue is sending a json payload from fiddler, I used the [fromBody] attribute but it does not work.  I would normally write something like { name: " john", age: 21} in the body of a POST request to update a database while doing demo but that does not work anymore.

Can you please give me an example how to do this.

Just a simple project

When I saw what webapi can do I was hooked

Thanks

Paul

 

 

Sep 14, 2012 at 10:37 AM

Paul,

What I think you are running into is that Web API in RTM doesn't do model binding out of the box of pieces within a body. That is, you can't model bind "name" and "age" as individual parameters to your POST method; rather you can model bind an *object* that has two properties: name and age. There are good reasons for this but if you want MVC style model binding then you can look at this blog post by Mike [0] as well as this one by Rick [1]

Thanks,

Henrik

[0] http://blogs.msdn.com/b/jmstall/archive/2012/04/18/mvc-style-parameter-binding-for-webapi.aspx

[1] http://www.west-wind.com/weblog/posts/2012/Sep/11/Passing-multiple-simple-POST-Values-to-ASPNET-Web-API