Jonathan Wood
Jonathan Wood

Reputation: 67175

Trouble with AJAX and data format

I have some code that uses jQuery's $.ajax().

I found that I had to pass my data through JSON.stringify().

$.ajax({
    url: '/Resource/ReportError',
    type: 'POST',
    data: JSON.stringify({
        ResourceId: popup.data('id'),
        Reason: reason,
        Description: $('#report-error-description').val(),
        Email: $('#report-error-email').val()
    }),
    contentType: 'application/json; charset=utf-8',
    success: function (data) {
        // ...
    },
    error: function () {
        // ...
    }
});

So far, so good. But now I'm using it to get some data, passing in only an ID. So I figured I should be using GET instead.

$.ajax({
    url: '/Resource/GetInitialReviewData',
    type: 'GET',
    data: JSON.stringify({ resourceId: resourceId }),
    contentType: 'application/json; charset=utf-8',
    success: function (data) {
        // ...
    },
    error: function () {
        // ...
    }
});

But this fails with an error about the resourceId being null on the server.

If I remove the call to JSON.stringify(), then it works!

data: { resourceId: resourceId },

Can anyone explain this in a way that's easy to understand? Why do I need JSON.stringify for POST but not for GET.

Upvotes: 1

Views: 92

Answers (2)

user3559349
user3559349

Reputation:

You do not need to use JSON.stringify() for a POST, although you can (but it is generally unnecessary unless your manually generating complex types or arrays which do not match the c# dot and indexer notation). The default contentType for the $.ajax() method is 'application/x-www-form-urlencoded; charset=UTF-8' which is essentially like a giant query string of the successful forms controls name/value pairs. To see what it would look like, your can use console.log($('form').serialize());.

If you omit the contentType then you can use the object without stringifying it.

$.ajax({
    url: '/Resource/ReportError',
    type: 'POST',
    data: {
        ResourceId: popup.data('id'),
        Reason: reason,
        Description: $('#report-error-description').val(),
        Email: $('#report-error-email').val()
    },
    success: function (data) {

The DefaultModelBinder will then use the FormValueProvider to read and bind the data to your method parameters.

When you use JSON.stringify() it converts your object to JSON-formatted text - a giant string containing the name/value pairs (notice the quotes around the resulting value) and you could receive this by having a single string parameter in your method and deserializing it yourself. By adding the contentType: 'application/json; charset=utf-8' the DefaultModelBinder will now use the JsonValueProviderFactory to read the string, deserialize it (using the JavaScriptSerializer) and bind the data to your method parameters.

In the case of a GET, there is no body, and the contentType option is not applicable (it will be ignored) as the DefaultModelBinder will only read the values from the query string (or route values). As noted in maddockst's answer, by using JSON.stringify() in your $.ajax() GET call, your generating a url of

.../Resource/GetInitialReviewData{"resourceId":"someValue"}

instead of the required

.../Resource/GetInitialReviewData?resourceId=someValue

Upvotes: 1

Thomas Maddocks
Thomas Maddocks

Reputation: 1385

The data attribute when used in conjunction with type: 'GET' will convert the value specified into a query string and append it to the URL. So if you called the URL http://www.test.com with data {name: 'Tom'}, you'd end up with the following URL:

http://www.test.com?name=Tom

If you pass a string, that string will be appended to the URL so the resulting URL when calling JSON.stringify would be:

http://www.test.com{"name":"tom"}

When you use data with type: 'POST', data is sent in the body of the request. The body of the request needs to be a string, the reason for the call to JSON.stringify.

Upvotes: 3

Related Questions