95

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)

norbitrial
  • 14,716
  • 7
  • 32
  • 59
Magmatic
  • 1,754
  • 3
  • 19
  • 34
  • This was the solution for me (use Javascript's XMLHttpRequest) while facing CORS issues with Ionic framework 3. – jeudyx Jul 04 '19 at 17:09

5 Answers5

75

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);
}
Will Mason
  • 776
  • 5
  • 5
  • 5
    Note that jQuery 1.5.2 has changed its behaviour. It no longer adds a "X-Requested-With" header, so this might no longer be an issue. http://blog.jquery.com/2011/03/31/jquery-152-released/ (Bug 8423) – Magmatic Apr 18 '11 at 15:18
  • 1
    @TimK, you are right! I did not notice they release 1.5.2. That being said, this also works if you need it to be pre-flighted. I've updated my answer. – Will Mason Apr 21 '11 at 00:37
  • So, I'm confused. You ended up having to write an intermediate PHP script anyway? So you don't need to worry about using Ajax then, right? Or am I missing something. Is there no JavaScript only solution? – Elisabeth May 30 '11 at 18:01
  • 1
    @Elisabeth This method only works if you control the requested destination...it is NOT an intermediate script. It is the top of our PHP of our requested location. Does that make more sense? – Will Mason Jun 26 '11 at 20:40
  • 2
    Yes! thanks Will. I thought you could control everything from the client side, but it sounds like you need control of both ends. – Elisabeth Jul 09 '11 at 00:34
  • I can confirm that jQuery 1.6 does not send any X- headers but that the preflight is still triggered - apparently because of the Content-Type request header - and which fails on any servers (like CouchDB) which don't implement the OPTIONS method. – Marc Oct 27 '12 at 22:03
  • is this header above a property of $.ajax()? – PositiveGuy Dec 15 '13 at 06:41
19

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:

1) Try configuring your server to send the proper preflight responses. This will be in the form of additional headers like Access-Control-Allow-Methods and Access-Control-Allow-Headers.

2) Drop the dataType: json setting. JQuery should request Content-Type: application/x-www-form-urlencoded by default, but just to be sure, you can replace dataType: json with contentType: 'application/x-www-form-urlencoded'

monsur
  • 45,581
  • 16
  • 101
  • 95
  • Thanks for the ideas. I tried not setting dataType, and setting it to be `application/x-www-form-urlencoded` and even `text/plain`. And I tried adding a response header of `Access-Control-Allow-Methods "POST, GET, OPTIONS"` Nothing worked. – Magmatic Apr 08 '11 at 19:27
  • Can you look in the JavaScript error console (or Firebug's console) and see if there are any errors during the request? Also, if you know how to use Wireshark, you can use that to see the actual HTTP requests going over the wire. – monsur Apr 08 '11 at 19:40
  • 1
    "Another possibility is that setting dataType: json causes JQuery to send the Content-Type: application/json header" — This does not happen. `dataType` influences the `Accept` request header but not the `Content-Type` request header. – Quentin Mar 26 '19 at 16:16
9

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
Aleadam
  • 40,203
  • 9
  • 86
  • 108
  • 1
    Sorry. Yes. `var data = {action:"something"}` – Magmatic Apr 07 '11 at 19:21
  • You can compare the syntax for both functions here: http://api.jquery.com/jQuery.post/ – Aleadam Apr 07 '11 at 19:34
  • I just tried it with the url in the settings, but same problem. The .ajax function can take it either way. – Magmatic Apr 09 '11 at 14:52
  • I already had two of those headers. I added the other two. Still "failure" with jQuery. The plain javascript still works. – Magmatic Apr 09 '11 at 19:28
  • The last thing I can think of is to use http://api.jquery.com/jQuery.ajaxSetup to set `jQuery.ajaxSetup({'beforeSend': function(xhr) {xhr.setRequestHeader(string, string)}})` and play with the different headers sent (an example for rails here: http://railscasts.com/episodes/136-jquery) – Aleadam Apr 09 '11 at 20:15
1

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", "*");
-2

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) {....}
});
Soma Sarkar
  • 836
  • 6
  • 7
  • Why would I want to make my Ajax callas synchronous!? – Radko Dinev Mar 26 '19 at 11:04
  • `contentType: 'application/json', data: JSONObject,` — The server isn't expecting JSON, so sending JSON wouldn't make sense. Also [There is no such thing as a JSON Object](http://benalman.com/news/2010/03/theres-no-such-thing-as-a-json/). – Quentin Mar 26 '19 at 16:14
  • 1
    `headers: { 'Access-Control-Allow-Origin': '*' }, //add this line` — **Never** do that. `Access-Control-Allow-Origin` is a **response** header, not a request header. At best this will do nothing. At worst it will convert the request from a simple request to a preflighted request which makes it evern harder to deal with on the server. – Quentin Mar 26 '19 at 16:15