I'm trying to build a simple API using Flask, in which I now want to read some POSTed JSON. I do the post with the PostMan Chrome extension, and the JSON I post is simply {"text":"lalala"}. I try to read the JSON using the following method:

@app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
    content = request.json
    print content
    return uuid

On the browser it correctly returns the uuid I put in the GET, but on the console, it just prints out None (where I expect it to print out the {"text":"lalala"}. Does anybody know how I can get the posted JSON from within the Flask method?

share
up vote 157 down vote accepted

You need to set the request content type to application/json for the .json property to work; it'll be None otherwise. See the Flask Request documentation:

If the mimetype is application/json this will contain the parsed JSON data. Otherwise this will be None.

Flask 0.10 added the request.get_json() method, and you should use that method instead of the .json property. You can tell the method to skip the content type requirement by setting force=True.

share
1  
Alright. And would you have any idea how to do that? – kramer65 Nov 15 '13 at 12:49
3  
@kramer65: How are you posting the request now? The client has to set the header; if you are using requests, that'd be request.post(url, headers={'Content-Type': 'application/json'}, data=json.dumps({'text': 'lalala'}). – Martijn Pieters Nov 15 '13 at 12:52
1  
Ah, now I understand. I had to set it at the sending party (i.e.: in PostMan). Okay, so I set that to json, but request.json is still a NoneType. ANy idea what else I could do wrong? – kramer65 Nov 15 '13 at 12:57
3  
@kramer65: See the source code; Either the Content-Type header is wrong, or the JSON you sent was 'null', which translates to None. Everything else raises an exception or returns your dictionary. Try request.get_json(force=True); this will ignore the header requirement. – Martijn Pieters Nov 15 '13 at 13:00
    
Okay! "I" fixed it! Using get_json(force=True) fixed it, but you were right all along; I set the content type in PostMan to JSON, but I didn't specifically set a header. So I now set the header and it all works fine. Thanks for the awesome help! – kramer65 Nov 15 '13 at 13:04

This is the way I would do it and it should be

@app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
    content = request.get_json(silent=True)
    print content
    return uuid

With silent=True set, the get_json function will fail silently when trying to retrieve the json body. By default this is set to False. Setting force=True will ignore the request.headers.get('Content-Type') == 'application/json' check that flask does for you. By default this is also set to False. See flask documentation.

I would strongly recommend leaving force=False and make the client send the Content-Type header to make it more explicit.

Hope this helps!

share
2  
Isn't it better to fail on errors?? – vidstige Oct 4 '16 at 9:02
    
Depends if the json body is optional or not, so depends on your case – radtek Oct 5 '16 at 18:37
1  
I cannot see any case where it would make sense to some times post valid json and other times invalid json. Sounds like two different end points – vidstige Oct 5 '16 at 18:39
    
Like I said, if an endpoint takes "optional" json body, you can use silent=True. Yes this is possible, and I do use it. Its really based on how you design your API to be consumed. If there is no case like that for your endpoint, just remove silent=True or explicitly set it to False. – radtek Oct 5 '16 at 18:57

For reference, here's complete code for how to send json from a Python client:

import requests
res = requests.post('http://localhost:5000/api/add_message/1234', json={"mytext":"lalala"})
if res.ok:
    print res.json()

The "json=" input will automatically set the content-type, as discussed here: Post JSON using Python Requests

And the above client will work with this server-side code:

from flask import Flask, request, jsonify
app = Flask(__name__)

@app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
    content = request.json
    print content['mytext']
    return jsonify({"uuid":uuid})

if __name__ == '__main__':
    app.run(host= '0.0.0.0',debug=True)
share
    
This actually didn't work for me. With python 2.7 if I specify the <uuid> argument the request gets denied with a 404. This is when sending a valid JSON POST with both Postman and a ReactJS application. If I omit the arg it works just fine. – Omortis Feb 23 at 20:28
1  
This example definitely works with Python 2.7. Make sure you actually have the "<" and ">" in the app.route. The left/right carets are part of the required Flask syntax. – Luke Feb 24 at 2:08
    
Yes, it does work. I was burying my request details in the JSON payload, not in the URI (as in: no arg supplied). Sorry for the static! – Omortis Feb 27 at 13:01

This solution works:

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/hello', methods=['POST'])
def hello():
   return jsonify(request.json)
share

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.