A CORS POST request works from plain JavaScript, but why not with jQuery?
JavascriptJqueryXmlhttprequestCorsJavascript Problem Overview
I'm trying to make a Cross Origin post request, and I got it working in plain JavaScript
like this:
var request = new XMLHttpRequest();
var params = "action=something";
request.open('POST', url, true);
request.onreadystatechange = function() {if (request.readyState==4) alert("It worked!");};
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader("Content-length", params.length);
request.setRequestHeader("Connection", "close");
request.send(params);
But I would like to use jQuery
, but I can't get it to work. This is what I'm trying:
$.ajax(url, {
type:"POST",
dataType:"json",
data:{action:"something"},
success:function(data, textStatus, jqXHR) {alert("success");},
error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});
This results in Failure. If anyone knows why jQuery
doesn't work, please let us all know. Thanks.
(I'm using jQuery
1.5.1, and Firefox 4.0, and my server is responding with a proper Access-Control-Allow-Origin
header)
Javascript Solutions
Solution 1 - Javascript
UPDATE: As TimK pointed out, this isn't needed with jquery 1.5.2 any more. But if you want to add custom headers or allow the use of credentials (username, password, or cookies, etc), read on.
I think I found the answer! (4 hours and a lot of cursing later)
//This does not work!!
Access-Control-Allow-Headers: *
You need to manually specify all the headers you will accept (at least that was the case for me in FF 4.0 & Chrome 10.0.648.204).
jQuery's $.ajax method sends the "x-requested-with" header for all cross domain requests (i think its only cross domain).
So the missing header needed to respond to the OPTIONS request is:
//no longer needed as of jquery 1.5.2
Access-Control-Allow-Headers: x-requested-with
If you are passing any non "simple" headers, you will need to include them in your list (i send one more):
//only need part of this for my custom header
Access-Control-Allow-Headers: x-requested-with, x-requested-by
So to put it all together, here is my PHP:
// * wont work in FF w/ Allow-Credentials
//if you dont need Allow-Credentials, * seems to work
header('Access-Control-Allow-Origin: http://www.example.com');
//if you need cookies or login etc
header('Access-Control-Allow-Credentials: true');
if ($this->getRequestMethod() == 'OPTIONS')
{
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Max-Age: 604800');
//if you need special headers
header('Access-Control-Allow-Headers: x-requested-with');
exit(0);
}
Solution 2 - Javascript
Another possibility is that setting dataType: json
causes JQuery to send the Content-Type: application/json
header. This is considered a non-standard header by CORS, and requires a CORS preflight request. So a few things to try:
-
Try configuring your server to send the proper preflight responses. This will be in the form of additional headers like
Access-Control-Allow-Methods
andAccess-Control-Allow-Headers
. -
Drop the
dataType: json
setting. JQuery should requestContent-Type: application/x-www-form-urlencoded
by default, but just to be sure, you can replacedataType: json
withcontentType: 'application/x-www-form-urlencoded'
Solution 3 - Javascript
You are sending "params" in js:
request.send(params);
but "data" in jquery". Is data defined?:
data:data,
Also, you have an error in the URL:
$.ajax( {url:url,
type:"POST",
dataType:"json",
data:data,
success:function(data, textStatus, jqXHR) {alert("success");},
error: function(jqXHR, textStatus, errorThrown) {alert("failure");}
});
You are mixing the syntax with the one for $.post
Update: I was googling around based on monsur answer, and I found that you need to add Access-Control-Allow-Headers: Content-Type
(below is the full paragraph)
http://metajack.im/2010/01/19/crossdomain-ajax-for-xmpp-http-binding-made-easy/
> How CORS Works > > CORS works very similarly to Flash's > crossdomain.xml file. Basically, the > browser will send a cross-domain > request to a service, setting the HTTP > header Origin to the requesting > server. The service includes a few > headers like > Access-Control-Allow-Origin to > indicate whether such a request is > allowed. > > For the BOSH connection managers, it > is enough to specify that all origins > are allowed, by setting the value of > Access-Control-Allow-Origin to *. The > Content-Type header must also be > white-listed in the > Access-Control-Allow-Headers header. > > Finally, for certain types of > requests, including BOSH connection > manager requests, the permissions > check will be pre-flighted. The > browser will do an OPTIONS request and > expect to get back some HTTP headers > that indicate which origins are > allowed, which methods are allowed, > and how long this authorization will > last. For example, here is what the > Punjab and ejabberd patches I did > return for OPTIONS: > > Access-Control-Allow-Origin: * > Access-Control-Allow-Methods: GET, POST, OPTIONS > Access-Control-Allow-Headers: Content-Type > Access-Control-Max-Age: 86400
Solution 4 - Javascript
Cors change the request method before it's done, from POST to OPTIONS, so, your post data will not be sent. The way that worked to handle this cors issue, is performing the request with ajax, which does not support the OPTIONS method. example code:
$.ajax({
type: "POST",
crossdomain: true,
url: "http://localhost:1415/anything",
dataType: "json",
data: JSON.stringify({
anydata1: "any1",
anydata2: "any2",
}),
success: function (result) {
console.log(result)
},
error: function (xhr, status, err) {
console.error(xhr, status, err);
}
});
with this headers on c# server:
if (request.HttpMethod == "OPTIONS")
{
response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
response.AddHeader("Access-Control-Max-Age", "1728000");
}
response.AppendHeader("Access-Control-Allow-Origin", "*");
Solution 5 - Javascript
Modify your Jquery in following way:
$.ajax({
url: someurl,
contentType: 'application/json',
data: JSONObject,
headers: { 'Access-Control-Allow-Origin': '*' }, //add this line
dataType: 'json',
type: 'POST',
success: function (Data) {....}
});