Tornado has correct request body, but cannot find

2019-07-09 00:33发布

问题:

Making a pretty straightforward Tornado app, but something that seems impossible is happening based on my understanding of Tornado. In short I've got the following RequestHandler:

class CreateUserHandler(tornado.web.RequestHandler):
    def post(self):
        print self.request.body
        print self.get_body_argument("email")
        # Do other things

And the following is what is printed:

{"email":"slater@indico.io","password":"password"}
WARNING:tornado.general:400 POST /user/create/ (::1): Missing argument email

So email clearly exists in the body, and yet when trying to access it a 400 is raised. I could manually parse the request body, but Tornado's error handling is nice enough that I'd like to avoid rewriting it if possible.

So, my basic question is how is this possible? It's printing the correct request body, and then is somehow unable to access the dictionary it just printed.

回答1:

get_body_argument is intended for form-encoded data, as you have discovered. Tornado has little support built-in for JSON data in request bodies. You can simply:

import json


class CreateUserHandler(tornado.web.RequestHandler):
    def post(self):
        data = json.loads(self.request.body)
        print data.get("email")


回答2:

Here's a little helper method I've been adding to my handlers in order to retrieve all of the body arguments as a dict:

from tornado.web import RequestHandler
from tornado.escape import json_decode

class CustomHandler(RequestHandler):

    def get_all_body_arguments(self):
        """
        Helper method retrieving values for all body arguments.

        Returns:
            Dict of all the body arguments.
        """
        if self.request.headers['Content-Type'] == 'application/json':
            return json_decode(self.request.body)
        return {arg: self.get_body_argument(arg) for arg in self.request.arguments}


回答3:

Not sure why this is the case, but I found a resolution. It required both url-encoding the body (this seems very strange to me as it's a request payload), and changing the Content-Type header to application/x-www-form-urlencoded

This is the new output of the script above:

email=slater%40indico.io&password=password
slater@indico.io