-
-
Save mminer/5464753 to your computer and use it in GitHub Desktop.
| import json | |
| import tornado.web | |
| class JsonHandler(BaseHandler): | |
| """Request handler where requests and responses speak JSON.""" | |
| def prepare(self): | |
| # Incorporate request JSON into arguments dictionary. | |
| if self.request.body: | |
| try: | |
| json_data = json.loads(self.request.body) | |
| self.request.arguments.update(json_data) | |
| except ValueError: | |
| message = 'Unable to parse JSON.' | |
| self.send_error(400, message=message) # Bad Request | |
| # Set up response dictionary. | |
| self.response = dict() | |
| def set_default_headers(self): | |
| self.set_header('Content-Type', 'application/json') | |
| def write_error(self, status_code, **kwargs): | |
| if 'message' not in kwargs: | |
| if status_code == 405: | |
| kwargs['message'] = 'Invalid HTTP method.' | |
| else: | |
| kwargs['message'] = 'Unknown error.' | |
| self.response = kwargs | |
| self.write_json() | |
| def write_json(self): | |
| output = json.dumps(self.response) | |
| self.write(output) |
There's a bug in this-- tornado expects the argument dictionary to be in the form of:
{
key: ['list', 'of', 'values'],
key: ['list', 'of', 'values'],
}Even if there's only one value for each key. This is to handle situations where we can pass multiple key-value pairs with the same key into a url querypath.
So if you post with {'message':'derp'} and use the built-in tornado function for getting arguments, you'll get this:
message = self.get_argument('message')
print message
> 'p'This is because tornado always gives you the last argument. -_-;
Additionally I pop the raw json out of the arguments dictionary. In the original version it stays there and it's ugly.
Here's my version of prepare()
def prepare(self):
'''Incorporate request JSON into arguments dictionary.'''
if self.request.body:
try:
json_data = json.loads(self.request.body)
for k, v in json_data.items():
# Tornado expects values in the argument dict to be lists.
# in tornado.web.RequestHandler._get_argument the last argument is returned.
json_data[k] = [v]
self.request.arguments.pop(self.request.body)
self.request.arguments.update(json_data)
except ValueError, e:
logger.debug(e.message)
message = 'Unable to parse JSON.'
self.send_error(400, message=message) # Bad Request
# Set up response dictionary.
self.response = dict()But overall: @mminer this is awesome and thanks for publishing this! As a tornado-noob it helped me a bunch.
Thanks for this. I am confused about the pop()? The body shouldn't be a key in the arguments dict. Also the logger fails without an import.
In Python 3, you might want to do json_data = tornado.escape.json_decode(self.request.body). self.request.body returns bytes in Python 3, and json.loads will not handle it automatically.
Thanks for the code.
@pconerly , The problem is that a number/float variables will not work well using your method.
@JohnBrodie Help me so much, but still no work on python3
This works for me:
for k,v in self.request.body_arguments.items():
body_kw[k] = str(v[0], encoding='utf-8')
if body_kw[k] == 'true':
body_kw[k] = True
if body_kw[k] == 'false':
body_kw[k] = False
print(body_kw)
Also note, that BaseHandler needs to be defined. Instead one can probably use tornado.web.RequestHandler.
From documentation: "write() is used for non-template-based output; it accepts strings, bytes, and dictionaries (dicts will be encoded as JSON)." So, may be better send self.response to self.write()?
One moment more is about writing json_data into self.request.arguments. Also like in documentation, may be better write its in self.json_args and use tornado.escape.json_decode(self.request.body) instead json.loads(self.request.body)?
No problem. You want your request handler to inherit from
JsonHandler. You then access the JSON request body using theself.request.argumentsdict and modify the response JSON using theself.responsedict.The response will be properly encoded as JSON with the appropriate
Content-Typeheader set. Additionally, if the request body is invalid JSON, the appropriate error code will be sent to the client.Hope that helps!