Search code examples
corstornado

Tornado server: enable CORS requests


I have a simple tornado server which has the class:

class BaseHandler(tornado.web.RequestHandler):
    def set_default_headers(self):
        print "setting headers!!!"
        self.set_header("Access-Control-Allow-Origin", "*")

When a regular (no CORS) request is made, the server answers as expected, including the Access-Control-Allow-Origin header. But when I make a post request coming from different domain (using jQuery.post), the response is 404 and an error is displayed: "XMLHttpRequest cannot load http://dev-machine:8090/handshake. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8090' is therefore not allowed access. The response had HTTP status code 404."

Can you tell if I miss something? (another header/other configuration/anything else)


Solution

  • Your code is missing preflight, the OPTIONS request.

    https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS:

    The Cross-Origin Resource Sharing standard works by adding new HTTP headers that allow servers to describe the set of origins that are permitted to read that information using a web browser. Additionally, for HTTP request methods that can cause side-effects on user data (in particular, for HTTP methods other than GET, or for POST usage with certain MIME types), the specification mandates that browsers "preflight" the request, soliciting supported methods from the server with an HTTP OPTIONS request method, and then, upon "approval" from the server, sending the actual request with the actual HTTP request method. Servers can also notify clients whether "credentials" (including Cookies and HTTP Authentication data) should be sent with requests.

    To implement preflight handler simply add options handler with the same headers and no body.

    class BaseHandler(tornado.web.RequestHandler):
    
        def set_default_headers(self):
            print "setting headers!!!"
            self.set_header("Access-Control-Allow-Origin", "*")
            self.set_header("Access-Control-Allow-Headers", "x-requested-with")
            self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
    
        def post(self):
            self.write('some post')
    
        def get(self):
            self.write('some get')
    
        def options(self, *args):
            # no body
            # `*args` is for route with `path arguments` supports
            self.set_status(204)
            self.finish()
    

    edit

    I've added x-requested-with header to allowed list. And here is simple jquery sample:

     $.ajax({
       url: "http://some_tornado/api",
       type: "POST",
       crossDomain: true,
       data: 'some_data',
       success: function (response) {
         alert(response);
       },
       error: function (xhr, status) {
         alert("error");
       }
     });
    

    And some really good article about cors - http://dev.housetrip.com/2014/04/17/unleash-your-ajax-requests-with-cors/