6

I am trying to set up simple Cross-Origin Resource Sharing using jQuery (1.7.1) powered ajax on the client and apache served python (django) server. According to all the instructions I have read my headers are set correctly, but I keep getting the following error:

XMLHttpRequest cannot load http://myexternaldomain.com/get_data. Origin http://localhost:8080 is not allowed by Access-Control-Allow-Origin.

The header being I am trying to (I am not sure it is even getting past the browser) send is:

Request URL:http://myexternaldomain.com/get_data
Accept:application/json, text/javascript, */*; q=0.01
Origin:http://localhost:8080
Referer:http://localhost:8080/static/js/test-zetta.html
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11

The javascript code is

    var request = $.ajax({
        url : "http://myexternaldomain.com/get_data",
        type : "POST",
        dataType : "json",
        crossDomain : true
    });

Note that origin is set correctly. The server adds the header Access-Control-Allow-Origin = * using the following python code

def process_response(self, response):
    if response.has_header('Access-Control-Allow-Origin'):
            return response

    response['Access-Control-Allow-Origin'] = '*'
    return response

def get_orders(request):
    """ Tell worker what to do """
    response_data = {}
    response_data['action'] = 'probe'
    response = process_response(HttpResponse(json.dumps(response_data), mimetype="application/json"))
    return response

If I visit the address directly, it appears to confirm that the header is being set correctly

Access-Control-Allow-Origin:*
Content-Type:application/json
Date:Thu, 08 Mar 2012 05:06:25 GMT
Server:Apache/2.2.20 (Ubuntu)
Transfer-Encoding:chunked

However in the cross domain setting it always fails (tried both chrome and firefox). I've tried implementing the code exactly as per the selected answer to this question, but get the same error

Update

I am quite sure that the problem is server side, as I have managed to get my ajax calls working with a different public CORS enabled server. When I compare the headers coming back from this public server, and the ones returned from mine (when I test from same domain), I cannot see any major difference which could account for difference (see below).

One subtlety that I excluded, which may or may be important is that the actual domain is an amazon domain of multiple subdomains. The real address is http://ec2-23-20-27-108.compute-1.amazonaws.com/get_orders , feel free to probe it to see what I am doing wrong.

From Public server

Access-Control-Allow-Origin:*
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:622
Content-Type:text/html
Date:Thu, 08 Mar 2012 15:33:20 GMT
Keep-Alive:timeout=15, max=99
Server:Apache/2.2.14 (Ubuntu)
Vary:Accept-Encoding
X-Powered-By:Perl/5.8.7, PHP/4.4.0

From my server - (not working cross domain)

Access-Control-Allow-Origin:*
Content-Encoding:gzip
Content-Type:text/plain
Date:Thu, 08 Mar 2012 15:32:24 GMT
Server:Apache/2.2.20 (Ubuntu)
Transfer-Encoding:chunked
Vary:Accept-Encoding
Community
  • 1
  • 1
zenna
  • 9,006
  • 12
  • 73
  • 101
  • 1
    I've tried, in the past, enabling CORS for Json data and never been successful. CORS can be flaky like that. You'd be much better off just returning JSON-P. – Ayush Mar 08 '12 at 06:36
  • You're only posting the headers from the _final_ exchange between your remote server and local client. If you look at the complete exchange, I'm sure you will see an OPTIONS request along with a response that contains Access-Control-Allow-Methods and Access-Control-Allow-Headers headers. – James Sumners Mar 10 '12 at 16:40

3 Answers3

5

You have to implement a "pre-flighted" request and response because your situation counts as a "not so simple" request. Basic CORS, that only requires the Origin header, can only have content types of "application/x-www-form-urlencoded", "multipart/form-data", and "text/plain". Since you return "application/json", you don't meet this requirement.

I don't know anything about Django, but I found it easier to implement CORS support outside of my application through the use of a Tomcat filter. It looks like you can do the same thing with Django.

2013-08-11: It looks like the GitHub repo is no longer with us. But the Django package looks to still be available at https://pypi.python.org/pypi/django-cors/0.1

James Sumners
  • 14,485
  • 10
  • 59
  • 77
  • Pretty sure the browser is supposed to take care of the preflight request....at least Chrome does. – JW. Mar 08 '12 at 06:26
  • But the server has to recognize it. His script only responds to basic requests by adding the Origin header to every request. – James Sumners Mar 08 '12 at 06:30
  • I tried making the request simple, by making my data a simple string, expecting plain text in the $.ajax call and returning mimetype='text/plain' in the python, but still get the same errors. – zenna Mar 08 '12 at 14:43
  • I've also managed to make it work with a public cors enabled server, so can be pretty confident in that my problem is server side. – zenna Mar 08 '12 at 15:13
  • Yes, that is exactly what I was saying. Your server side script is not handling the pre-flight type of requests. Based on the code you have provided, your script will only work with basic CORS requests. – James Sumners Mar 09 '12 at 01:57
  • Further, if you return the string '{"foo":"bar"}' to jQuery as "text/plain", it might fail because it recognizes the string as JSON. I'm not sure about that one, though. – James Sumners Mar 09 '12 at 01:58
  • 1
    I can't overstate the awesomeness of this answer. It's dead-on. – Adam Tuttle Jun 12 '13 at 19:22
5

So I was being mislead by the response from going to the URL, and in fact the problem was that when doing the ajax request, I was getting a 403 (only revealed in firefox not chrome) error due to csrf protection.

zenna
  • 9,006
  • 12
  • 73
  • 101
  • 1
    Oh man, thanks so much. I had my `enable_cors` wrapper around my `csrf_exempt` wrapper, needed to reverse them. I was like, WTF... – orokusaki Apr 12 '13 at 01:56
2

I was using the excellent django-cors-headers library and ran into this problem as well. For me, the solution was to add 'accept-encoding' to the default CORS_ALLOW_HEADERS tuple.

rattray
  • 5,174
  • 1
  • 33
  • 27