"WARNING:tornado.access:405" error stopping POST from both "localhost" and "file://" origins
Asked Answered
T

2

2

This is a very similar problem to this question (Tornado POST 405: Method Not Allowed), but the dead simple answer for that question is still not working. There are also many, many more similar questions in the side bar -----> which are not related to Tornado, and did not provide me with a solution.

Right now I am using Firefox on OSX.

My tornado code is as follows:

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def post(self):
        self.write("Hello, world")
    get = post # <--------------

application = tornado.web.Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

If I run GET, it works fine, but if I use POST, I get the error on client side HTTP/1.1 405 Method Not Allowed and the error on server side WARNING:tornado.access:405 OPTIONS. I have tried running the js in both a file://index.html and localhost:8000/index.html setting

My test js looks like this:

//this one returns the error
U.Ajax.post("http://localhost:8888/", "text", "data = fez hat")
.then(function(result) {
    console.log(result);
});

//this one works
U.Ajax.get("http://localhost:8888/", "text", "data = fez hat")
.then(function(result) {
    console.log(result);
});

My ajax code looks like this, if that's any help:

//tested and functional ajax code, for ease of testing
U = {};
U.Ajax = {};
U.Ajax.send = function(getOrPost, url, dataType, data) {
    return new Promise( function(resolve, reject) {
        var request = new XMLHttpRequest();

        if(getOrPost == "GET") {
            request.responseType = dataType || "text";
            request.open("GET", url);   
        }
        else {//getOrPost == "POST"
            request.open("POST", url);  
            request.setRequestHeader('Content-type', dataType)
        }


        request.onload = function() {
            if (request.status >= 200 && request.status < 400) {
                console.log("ajax", request.status+" "+url, request);

                resolve(request);               

            } else {
                request.onerror();
            }
        };

        request.onerror = function() {
            var err = "include src '"+url+"' does not exist";
            console.log(err)
            reject(err)
        };

        try {
            request.send(data); 
        }
        catch(e) {
            var err = "NS_ERROR_DOM_BAD_URI: Access to restricted URI '"+url+"' denied";
            console.log(err)
            reject(err)
        }


    });
}

U.Ajax.get = function(url, responseType) {
    return U.Ajax.send("GET", url, responseType);
}

U.Ajax.post = function(url, contentType, data) {
    return U.Ajax.send("POST", url, contentType, data);
}

EDIT :: If I change the tornado code to equate GET POST and OPTION, it works, but incorrectly

class MainHandler(tornado.web.RequestHandler):
    def post(self):
        print(self.request)
    get = options = post # <--------------

I no longer get an error, but when I print self.request it appears that my headers are set to "OPTIONS" somehow

HTTPServerRequest(protocol='http', host='localhost:8888', method='OPTIONS', uri='/', version='HTTP/1.1', remote_ip='127.0.0.1', headers={'Origin': 'null', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate', 'Access-Control-Request-Headers': 'content-type', 'Host': 'localhost:8888', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:55.0) Gecko/20100101 Firefox/55.0', 'Access-Control-Request-Method': 'POST', 'Connection': 'keep-alive'})

Transarctic answered 4/7, 2017 at 8:2 Comment(7)
What javascript library are you using? U.Ajax?Mansion
No, it's pretty much copied from this #8567614Transarctic
“but when I print self.request it appears that my headers are set to "OPTIONS" somehow” – ... which likely just means you are looking at the pre-flight request that such cross-domain requests require.Underwood
You'd better check, whether OPTIONS request is sent from browser (using Developer Tools of crhome or similar tools). I think it's javascript issue.Mansion
@Mansion I just tried some different barebones ajax code, and same resultTransarctic
Instead of using ajax from another domain, try to access the page directly using browser. localhost:8888Mansion
@Mansion that one has been working from the start. Does what one would expect, which was "hello world".Transarctic
U
3

I no longer get an error, but when I print self.request it appears that my headers are set to "OPTIONS" somehow

That would be the CORS pre-flight request.

https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request:

A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood.

It is an OPTIONS request using two HTTP request headers: Access-Control-Request-Method and Access-Control-Request-Headers, and the Origin header.

A preflight request is automatically issued by a browser when needed; in normal cases, front-end developers don't need to craft such requests themselves.

For cross-domain AJAX requests, the browser first needs to check with the remote server, whether it wants to accept a request using a specific method and/or specific request headers.

That happens via an OPTIONS request. And when the server signals in the response that the actual request the client wants to make is acceptable, the client then makes that request.

Underwood answered 4/7, 2017 at 8:39 Comment(0)
F
2

Recently, I have met the same problem. I solved it using the following code:

import tornado.ioloop 
import tornado.web

class MainHandler(tornado.web.RequestHandler): 
    def set_default_headers(self):
        print('set headers!!')
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Headers', '*')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Content-type', 'application/json')
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers',
                        'Content-Type, Access-Control-Allow-Origin, Access-Control-Allow-Headers, X-Requested-By, Access-Control-Allow-Methods')
    def OPTIONS(self):
        pass

application = tornado.web.Application([ (r"/", MainHandler), ])    
if name == "main":application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
Fe answered 11/6, 2018 at 9:14 Comment(4)
This worked for me but I had to make options lowercase.Guanabana
For other readers: be wary of including Content-type': 'application-json' : that may not be appropriate for your appLachellelaches
@Guanabana which options? I don't know how to follow your suggestion.Lachellelaches
@javadba def options(self):Guanabana

© 2022 - 2024 — McMap. All rights reserved.