Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass complex model from client to server?

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

like image 939
Neil Avatar asked Aug 02 '12 09:08

Neil


1 Answers

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.

like image 90
Andras Zoltan Avatar answered Sep 24 '22 14:09

Andras Zoltan