8

I have some data "Foo" that I want to pass from the browser to the server and retrieve predicted statistics based on the information contained within foo.

$.ajax({
      type: 'GET',
      url: "/api/predictedStats/",
      data: "foo=" + ko.toJSON(foo, fooProperties),
      contentType: 'application/json; charset=utf-8',
      dataType: 'json',
      success: function(data) {
        return _this.viewModel.setPredictedStats(data);
      },
      error: function(jqXHR, statusText, errorText) {
        return _this.viewModel.setErrorValues(jqXHR, errorText);
      }
    });

I created a predicted stats controller and get method taking an argument of Foo.

public class PredictedStatsController : ApiController
{
    public PredictedStats Get(Foo foo)
    {
        return statsService.GetPredictedStats(foo);
    }
}

Sticking a breakpoint on the Get method I see the Foo object is always null. There are no errors thrown from the webapi trace logging just the following lines.

WEBAPI: opr[FormatterParameterBinding] opn[ExecuteBindingAsync] msg[Binding parameter 'foo'] status[0]  
WEBAPI: opr[JsonMediaTypeFormatter] opn[ReadFromStreamAsync] msg[Type='foo', content-type='application/json; charset=utf-8'] status[0]  
WEBAPI: opr[JsonMediaTypeFormatter] opn[ReadFromStreamAsync] msg[Value read='null'] status[0]   

I've no problem sending the data via a post to the Foo controller to create the Foo object on the server so I can say there's nothing wrong with the json created clientside.

Looking in fiddler the resulting Get looks like the following where jsondata is the object foo.

GET /api/predictedStats?foo={jsondata} HTTP/1.1

Is this even possible or am I going about this all wrong?

Thanks Neil


EDIT: I feel like I almost got this working with the following

public PredictedStats Get([FromUri]Foo foo)
{
    return statsService.GetPredictedStats(foo);
}

The object foo was coming back fine but no properties of Foo were being populated properly.


In the mean time I've resorted to using a POST with near identical data just dropping the "foo=" and this is working just fine.

I'm not sure whether the POST or the GET should be used in this case but that would be interesting to know.


I also found this http://bugs.jquery.com/ticket/8961 which seems to suggest you can't attach a body to a GET request with jquery so POST is probably the only sensible option

abatishchev
  • 92,232
  • 78
  • 284
  • 421
Neil
  • 4,949
  • 7
  • 42
  • 82

1 Answers1

6

You almost got there :)

When you use [FromUri] (which you have to use for 'complex' objects because by default Web API doesn't 'bind' complex objects, it's always looking to deserialize them from the body) you don't need to pass param= in the Uri - you just pass the members of the value as query string parameters. That is 'member1=value&member2=value' - where member1 and member2 are members of the Foo.

Note there is no 'bug' in jQuery - while the HTTP spec doesn't prohibit a request body, it's likely that the browser does (and if that's the case, the jQuery can't send it), and it's more than likely that a server will never read it anyway. It's just not accepted practise. It also has interesting issues with caching potentially, as well, in that a browser won't cache a POST, PUT, DELETE etc, but will cache a GET if the response headers don't prohibit it - that could have serious side effects for a client application. I recommend you look at this SO: HTTP GET with request body for more information and some useful links on this subject.

Equally, when using jQuery - you don't need to convert the object to JSON either - just pass the javascript object in the data member of the options and jQuery turns it into the correct format.

Or should that be, Web API understands the format the jQuery passes it as.

Community
  • 1
  • 1
Andras Zoltan
  • 40,853
  • 11
  • 98
  • 156
  • 1
    Thanks, I was scratching my head why a 'complex object' parameter wasn't populated although I decorated it with `[FromUri]`. I tried to pass in json e.g. ?param={Name: Foo} which didn't work, I had to change it to ?Name=Foo as you said. Note that if you have multiple `[FromUri]` complex object parameters, you need to make sure they don't have properties with the same name - or you'll end up with the same property value assigned to both instances. – Alexander Köplinger Sep 01 '12 at 17:53
  • 2
    Please note that a simple `public string member1;` will not work and `member1` still will be `null`. Turn them into **properties** and it will work fine. – Thomas Oct 14 '12 at 20:47
  • about them having to be property...why did I not read this 4 hours ago. talk about feeling stupid, thanks Thomas for this simple yet oh so perfect comment. – hubson bropa Jun 21 '13 at 15:34
  • Dont forget to do the /? or use ? right after your api route because it will not work e.g. url:port/apiroute/?'member1=value&member2=value' will work... if you forget the ? it will not... – Louie Bacaj Oct 09 '14 at 21:31