Bug Tracker

Opened 12 years ago

Closed 12 years ago

Last modified 10 years ago

#8417 closed bug (fixed)

When posting AJAX and the data has "??" is formats it to jQuery<timestamp>?

Reported by: jason.jong@… Owned by: jaubourg
Priority: low Milestone: 1.6
Component: ajax Version: 1.5.1
Keywords: Cc:
Blocked by: Blocking:

Description

Works fine with 1.4.4

Example:

var myData = { AccountIds: 17, Message: "??" }; var encData = JSON.stringify(myData);

all ok so far $.ajax({

type: "POST", url: "services/Messages.ashx?cmd=set", data: jsonData, dataType: "json", success: function() { }

});

in the receiving end, then I read the stream, the message becomes

jQuery151044947751238942146_1299030184872?

Change History (47)

comment:1 Changed 12 years ago by jason.jong@…

i tried to replicate the issue on jsfiddle, but dont know how to see the data posted back

http://jsfiddle.net/aVddJ/3/

comment:2 Changed 12 years ago by jaubourg

Resolution: wontfix
Status: newclosed

Default encoding in ajaxSettings is x-www-form-urlencoded. So you have to encode your data using encodeURIComponent().

See https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURIComponent

comment:3 Changed 12 years ago by shiki

The weird thing about this is if I set the ajax settings to:

contentType: "application/json; charset=utf-8",
dataType: "json",
processData: false,

The "??" string in data still gets replaced with something like "jQuery151020082441558501163_1300269082410". If I'll remove "dataType: json" though, it works.

Sample: http://jsfiddle.net/5gwEF/
(I used Firebug to see what the ajax sending). This seems to work with 1.4.4.

I'm curious on what's really the correct way to send a JSON string with $.ajax. Encoding with encodeURIComponent() would turn "??" into "%20%3F%3F". That's what the server's gonna receive. Is the server responsible for decoding it (e.g. urldecode) even if the sent content type is "application/json"?

Interestingly, that code in jsFiddle is pretty much how Backbone uses it to send JSON to the server. Direct link here: http://documentcloud.github.com/backbone/docs/backbone.html#section-111

comment:4 Changed 12 years ago by jaubourg

#8697 is a duplicate of this ticket.

comment:5 Changed 12 years ago by anonymous

So will this be fixed?

Is the solution to encodeURIComponent() all the inputs and then URLDecode on the server?

comment:6 Changed 12 years ago by anonymous

Any update on this ticket? I keep running into this issue and not sure how to work around it.

Should we call encodeURIComponent() on the json string or will this be fixed in jquery?

Thanks.

comment:7 Changed 12 years ago by Jason Davies

This is happening for me too. As mentioned in the 3rd comment, the workaround is to remove dataType: "json". I'm not sure why this replacement is occurring here, I'm guessing this is related to JSONP.

I suggest reopening - it doesn't seem to be related to using encodeURIComponent as that would only apply for a content type of x-www-form-urlencoded. This can be reproduced for a content type of application/json.

comment:8 Changed 12 years ago by cowboy

Guys, the data that $.ajax expects is either a query string, like a=1&b=2 or an object (that $.ajax serializes into a query string internally, using $.param) like {a: 1, b: 2}.

By passing a JSON string in as form data, you're effectively requesting a URL like:

process.php?{"a":1,"b":2}

which makes no sense. Note that both GET and POST work in a similar enough way for the purpose of this comment that you shouldn't worry about the semantics. Either way, the server is really expecting a URL like:

process.php?a=1&b=2

The solution: just pass the object in as data, without first stringifying it to JSON, and you should be all set. See this example.

comment:9 Changed 12 years ago by ajpiano

OR if you actually WANT to pass a JSON string to the server, for whatever reason, you have to pass it as one of the values in the data parameter

$.ajax({
 data: {
  foo: JSON.stringify(bar);
 }
});

comment:11 Changed 12 years ago by Jason Davies

Hi all, thanks for taking a look. I'm not too familiar with jsFiddle but I managed to reproduce the problem in this one: http://jsfiddle.net/zs4Ez/5/

Note that you have to use the "Resources" tab in Webkit to view the request payload that's being sent to the server to see the problem. Note that I am using JSON.stringify() for the data.

comment:12 in reply to:  11 ; Changed 12 years ago by Timmy Willison

Replying to Jason Davies:

Yes, as the previous commenters explained, your data is not valid. You can do one of the other two options.

comment:13 in reply to:  12 Changed 12 years ago by Jason Davies

Replying to timmywil:

Hi Timmy, thanks for looking at this.

The data itself is not valid, perhaps it'll help if I explain the use case. I'm simply trying to POST a string (containing JSON data) as the request body. This is common for REST APIs that take JSON data as input via the POST body.

It seems the default setup is to replace all instances of ?? in the POST body with a callback, even for normal dataType="json" requests. Or perhaps I'm misunderstanding the point of the dataType parameter? From the docs: "The type of data that you're expecting back from the server." - doesn't seem relevant.

The thing is, my request is *not* a JSONP request, so I don't know why this ?? replacement is occurring at all.

comment:14 Changed 12 years ago by Jason Davies

I know I can turn this behaviour off by setting jsonp: false, but it seems odd that JSONP replacement occurs by default even when JSONP is not being used.

comment:15 Changed 12 years ago by Timmy Willison

Of course. REST! However, you can ensure that requests are not converted to jsonp and override the addition of a callback by setting jsonp to false. Click the post in firebug lite and go the POST tab to view the data. http://jsfiddle.net/timmywil/zs4Ez/6/

It should be more restful. ;)

comment:16 Changed 12 years ago by Timmy Willison

Well, it seems you figured that out. The automatic conversion to jsonp exists so that the getJSON function (which is really just a shortcut for ajax as you know) can also be used for jsonp.

comment:17 Changed 12 years ago by Jason Davies

See https://github.com/jquery/jquery/pull/294 for my fix, which only affects requests with data in the request body, so won't affect getJSON.

comment:18 Changed 12 years ago by ajpiano

Resolution: wontfix
Status: closedreopened

comment:19 Changed 12 years ago by ajpiano

Component: unfiledajax
Priority: undecidedlow

comment:20 in reply to:  17 Changed 12 years ago by jaubourg

Owner: set to jaubourg
Status: reopenedassigned

Replying to Jason Davies:

See https://github.com/jquery/jquery/pull/294 for my fix, which only affects requests with data in the request body, so won't affect getJSON.

I commented on this request and closed it.

shiki exhibited the actual bug in comment 3 ( http://bugs.jquery.com/ticket/8417#comment:3 ): if contentType is x-www-form-urlencoded then you have to use encodeURIComponent when providing data as a string:

$.ajax({
 data: encodeURIComponent( JSON.stringify(bar) )
});

In that case, the regexp is never positive and, since the encoding is provided in the request headers, the server will decode the data string properly.

Now, when contentType does not involve url encoding, then, yes, here you have an actual bug since the prefilter shouldn't attempt to regexp test the data.

jQuery 1.4.4 was far too lenient regarding encoding. If contentType is x-www-form-urlencoded, you have to url-encode your data, or else, ajax is perfectly entitled to interpret special characters as it sees fit. Once again: encoding is NOT optional.

Didn't see the conversation was still going and missed shiki's comment which, again, was right on while acknowledging the need for encoding when specified. I'm taking ownership.

comment:21 Changed 12 years ago by Jason Davies

I've updated the pull request with a fix that checks for the contentType instead: https://github.com/jquery/jquery/pull/294

comment:22 Changed 12 years ago by ajpiano

#8798 is a duplicate of this ticket.

comment:23 Changed 12 years ago by jaubourg

#8895 is a duplicate of this ticket.

comment:24 Changed 12 years ago by jaubourg

Resolution: fixed
Status: assignedclosed

https://github.com/jquery/jquery/commit/4ad9b44deada9da9639f53b1ca3cc4cf2ebf2df2

Use proper encoding or set it properly using contentType.

comment:25 Changed 12 years ago by jaubourg

Milestone: 1.next1.6

comment:26 Changed 12 years ago by Rick Waldron

#9032 is a duplicate of this ticket.

comment:27 Changed 12 years ago by Rick Waldron

#9340 is a duplicate of this ticket.

comment:28 Changed 12 years ago by jaubourg

#9430 is a duplicate of this ticket.

comment:29 Changed 12 years ago by dgrobler

Still getting the same issue with 1.6.1 - explicitly specifying contentType as "application/json; charset=utf-8".

Sending data to REST endpoint (Microsoft Dynamics CRM 2011 oData REST endpoint to be exact)

comment:30 in reply to:  29 ; Changed 12 years ago by jaubourg

Replying to dgrobler:

Still getting the same issue with 1.6.1 - explicitly specifying contentType as "application/json; charset=utf-8".

Sending data to REST endpoint (Microsoft Dynamics CRM 2011 oData REST endpoint to be exact)

Could you make a fiddle demonstrating the issue?

comment:31 in reply to:  30 Changed 12 years ago by dgrobler

Replying to jaubourg:

Replying to dgrobler:

Still getting the same issue with 1.6.1 - explicitly specifying contentType as "application/json; charset=utf-8".

Sending data to REST endpoint (Microsoft Dynamics CRM 2011 oData REST endpoint to be exact)

Could you make a fiddle demonstrating the issue?

Tricky putting a working fiddle together (need to be able to authenticate against a Microsoft Dynamics CRM 2011 oData REST endpoint) What I can do is post the following that might help:

jQuery AJAX Code

    $.ajax(
    {
        type: "POST",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        url: serverUrl + ODATA_ENDPOINT + "/" + odataSetName,
        data: jsonEntity,
        beforeSend: function (XMLHttpRequest) {
            //Specifying this header ensures that the results will be returned as JSON.             
            XMLHttpRequest.setRequestHeader("Accept", "application/json; charset=utf-8");
        },
        success: function (data, textStatus, XmlHttpRequest) {
            if (successCallback) {
                successCallback(data.d, textStatus, XmlHttpRequest);
            }
        },
        error: function (XmlHttpRequest, textStatus, errorThrown) {
            if (errorCallback)
                errorCallback(XmlHttpRequest, textStatus, errorThrown);
            else
                CaseErrorHandler(XmlHttpRequest, textStatus, errorThrown);
        }
    });

JSON that is passed into the data parameter:

"{"CustomerId":{"Id":"{6E89243F-111D-E011-872A-00505682001B}","Name":"Interglobal","LogicalName":"account"},"Title":"John Bain","new_UserStory":null,"CaseTypeCode":{"Value":2},"new_CaseStatus":{"Value":100000000},"new_StartTime":null,"new_EndTime":null,"new_ClientId":{"Id":"{6E89243F-111D-E011-872A-00505682001B}","Name":"Interglobal","LogicalName":"account"},"new_CustomerPolicyId":{"Id":"{ECE85351-8D7B-E011-8686-00505682001B}","Name":"John Bain - ABC123","LogicalName":"new_customerpolicy"},"new_City":null,"new_Country":null,"new_CustomerId":{"Id":"{A913AB1A-3196-E011-8D9F-00505682001B}","Name":"John Lawrie Bain","LogicalName":"contact"},"new_Handover":true,"new_HandoverReasonDetails":"Test jquery 1.6.1 with lots of question marks","new_HandoverScript":"Test jquery 1.6.1 with lots of question marks??","new_HandoverToId":{"Id":"{24CE0524-BC34-E011-872A-00505682001B}","Name":"Dieter Grobler","LogicalName":"systemuser"},"new_HandoverReason":{"Value":100000000},"new_voicemailsent":false,"new_Whiteboard":true,"new_WhiteboardReasonDetails":"Test jquery 1.6.1 with lots of question marks","new_WhiteboardReason":{"Value":100000000},"new_FinancialCaseStrategy":null,"new_FinancialCaseStrategyUpdated":null,"new_LogisticalCaseStrategy":null,"new_LogisticalCaseStrategyUpdated":null,"new_MedicalCaseStrategy":null,"new_MedicalCaseStrategyUpdated":null,"TransactionCurrencyId":{"Id":"{4E87F4A0-041D-E011-B306-00505682001B}","Name":"New Zealand Dollar","LogicalName":"transactioncurrency"},"new_ReserveAmount":{"Value":"5000"},"new_RequireEvacuationRepatriation":false}"

Request JSON:

{"CustomerId":{"Id":"{6E89243F-111D-E011-872A-00505682001B}","Name":"Interglobal","LogicalName":"account"},"Title":"John Bain","new_UserStory":null,"CaseTypeCode":{"Value":2},"new_CaseStatus":{"Value":100000000},"new_StartTime":null,"new_EndTime":null,"new_ClientId":{"Id":"{6E89243F-111D-E011-872A-00505682001B}","Name":"Interglobal","LogicalName":"account"},"new_CustomerPolicyId":{"Id":"{ECE85351-8D7B-E011-8686-00505682001B}","Name":"John Bain - ABC123","LogicalName":"new_customerpolicy"},"new_City":null,"new_Country":null,"new_CustomerId":{"Id":"{A913AB1A-3196-E011-8D9F-00505682001B}","Name":"John Lawrie Bain","LogicalName":"contact"},"new_Handover":true,"new_HandoverReasonDetails":"Test jquery 1.6.1 with lots of question marks","new_HandoverScript":"Test jquery 1.6.1 with lots of question marksjQuery15102242055933082286_1308190892929","new_HandoverToId":{"Id":"{24CE0524-BC34-E011-872A-00505682001B}","Name":"Dieter Grobler","LogicalName":"systemuser"},"new_HandoverReason":{"Value":100000000},"new_voicemailsent":false,"new_Whiteboard":true,"new_WhiteboardReasonDetails":"Test jquery 1.6.1 with lots of question marks","new_WhiteboardReason":{"Value":100000000},"new_FinancialCaseStrategy":null,"new_FinancialCaseStrategyUpdated":null,"new_LogisticalCaseStrategy":null,"new_LogisticalCaseStrategyUpdated":null,"new_MedicalCaseStrategy":null,"new_MedicalCaseStrategyUpdated":null,"TransactionCurrencyId":{"Id":"{4E87F4A0-041D-E011-B306-00505682001B}","Name":"New Zealand Dollar","LogicalName":"transactioncurrency"},"new_ReserveAmount":{"Value":"5000"},"new_RequireEvacuationRepatriation":false}

Note that my question marks (??) are replaced by jQuery15102242055933082286_1308190892929 in my request. The server does respond as expected with the correct result but the jQuery call triggers the error event handler rather than success.

Hope that helps and I hope that it is not my stupid mistake!

comment:32 Changed 11 years ago by anonymous

I'm using jQuery 1.8 and the problem is still there. Using '??' makes jQuery replace that with the string generated by:

jQuery.ajaxSetup({

jsonp: "callback", jsonpCallback: function() {

return jQuery.expando + "_" + ( jsc++ );

}

});

comment:33 Changed 11 years ago by swdc17@…

Please re-open this ticket, the problem still exists.

comment:34 in reply to:  33 Changed 11 years ago by anonymous

Replying to swdc17@…:

Please re-open this ticket, the problem still exists.

comment:35 Changed 11 years ago by alec

Still not fixed in 1.8.2.

comment:36 Changed 11 years ago by alec

Forget about my comment. Setting contentType to "application/json; charset=utf-8" fixes the issue.

comment:37 Changed 11 years ago by erlend@…

Compare these two: http://jsfiddle.net/FyRBK/3/ http://jsfiddle.net/FyRBK/4/

Setting the content type in different ways, gives different results.

This is still a bug, and should be reopened.

comment:38 in reply to:  37 Changed 11 years ago by jaubourg

Replying to erlend@…:

Compare these two: http://jsfiddle.net/FyRBK/3/ http://jsfiddle.net/FyRBK/4/

Setting the content type in different ways, gives different results.

This is still a bug, and should be reopened.

Scrap the previous comment. I wasn't quite awake.

So, jQuery doesn't inspect headers sent the second way because some transports won't use them. Use contentType to set the encoding of your data. It's not a bug.

Last edited 11 years ago by jaubourg (previous) (diff)

comment:39 Changed 10 years ago by erlend@…

replying to jaubourg I humbly disagree. It is a bug. Why is changing the _body_ sent in a _POST_ something jQuery should do? When is this useful? The principle of least surprise.

comment:40 Changed 10 years ago by jo.jcat@…

We recently ran into an issue not unlike this one. The type was explicitly set to POST, but was silently converted to GET inside the inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); I understand the body conversion when setting the type to JSONP, but if the type is still JSON (set explicitly) and the method is POST (set explicitly), the conversion from POST to GET should not happen in the presence of a double question mark.

comment:41 Changed 10 years ago by anonymous

Seems to come down to "Handle cache's special case and global." prefilterOrFactory() is doing the actual swap if s.crossDomain is set. I can't find the location where prefilterOrFactory is defined or the conditions under which it is added to the handler.

One thing remains clear: jQuery.ajaxPrefilter (and, subsequently, a conversion from POST to GET) ONLY invoked if there's a double '??' in the data string.

comment:42 Changed 10 years ago by jaubourg

#13766 is a duplicate of this ticket.

comment:43 Changed 10 years ago by jaubourg

#13913 is a duplicate of this ticket.

comment:44 Changed 10 years ago by anonymous

So what is the solution for this bug ?

comment:45 in reply to:  44 Changed 10 years ago by anonymous

Replying to anonymous:

So what is the solution for this bug ?

Maybe old information. But alec's solution works fine for me. I.e. setting contentType to "application/json; charset=utf-8"

comment:46 Changed 10 years ago by anonymous

Still happening in 1.8.3

comment:47 Changed 10 years ago by sean.baker@…

I just ran into this issue as well - using jQuery 1.8.2

I added jsonp: false as suggested by Jason Davies above for now as a work-around.

I am posting a JSON string as data, using settings processData: false and dataType: 'json'. This has worked for a long time for me, but I just discovered it fails if any field ever has a double quotation mark in it as noted by others above.

Note: See TracTickets for help on using tickets.