Search code examples
javascriptjquerypythonjsontornado

Jquery POST JSON data to Python backend


I would like to send some data from jQuery to a Tornado Python backend.

Here is simple example:

$.ajax({
    url: '/submit_net',
    dataType: 'json',
    data: JSON.stringify({"test_1":"1","test_2":"2"}),
    type: 'POST',
    success: function(response) {
        console.log(response);
    },
    error: function(error) {
        console.log(error);
    }

});

Here is the Python code:

class submit_net(tornado.web.RequestHandler):
    def post(self):
        data_json = self.request.arguments
        print data_json

When I click the submit button then the Python backend retrieves following dictionary

{'{"test_1":"1","test_2":"2"}': ['']}

but I would like to retrieve exactly the same dictionary as jQuery sends:

{"test_1":"1","test_2":"2"}

Could you help me what I am doing wrong?


Solution

  • request.arguments should only be used for form encoded data. Use request.body to access the JSON raw data and decode with the json module:

    import json
    
    data_json = self.request.body
    data = json.loads(data_json)
    

    request.body contains bytes, which is fine in Python 2, but if you are using Python 3 you need to decode those to Unicode first. Get the request character-set with cgi.parse_header():

    from cgi import parse_header
    
    content_type = self.request.headers.get('content-type', '')
    content_type, params = parse_header(content_type)
    charset = params.get('charset', 'UTF8')
    data = json.loads(data_json.decode(charset))
    

    This defaults to the UTF-8 character set, which as a default is only valid for JSON requests; other request content types would need to be handled differently.

    You may want to make it clear that you are sending a JSON body by setting the content type:

    $.ajax({
        url: '/submit_net',
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify({"test_1":"1","test_2":"2"}),
        type: 'POST',
        success: function(response) {
            console.log(response);
        },
        error: function(error) {
            console.log(error);
        }
    });
    

    and verify in your Tornado POST handler that that content type is being used before trying to decode the POST as JSON:

    content_type = self.request.headers.get('content-type', '')
    content_type, params = parse_header(content_type)
    if content_type.lower() != 'application/json':
        # return a 406 error; not the right content type
        # ...
    
    charset = params.get('charset', 'UTF8')
    data = json.loads(data_json.decode(charset))
    

    The $.ajax dataType parameter is only needed when you are returning JSON from Python back to jQuery; it tells jQuery to decode the response for you. This is not strictly needed even then, as a application/json response Content-Type header is enough.