495

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?

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
kramer65
  • 50,427
  • 120
  • 308
  • 488

13 Answers13

622

First of all, the .json attribute is a property that delegates to the request.get_json() method, which documents why you see None here.

You need to set the request content type to application/json for the .json property and .get_json() method (with no arguments) to work as either will produce None otherwise. See the Flask Request documentation:

The parsed JSON data if mimetype indicates JSON (application/json, see .is_json).

You can tell request.get_json() to skip the content type requirement by passing it the force=True keyword argument.

Note that if an exception is raised at this point (possibly resulting in a 400 Bad Request response), your JSON data is invalid. It is in some way malformed; you may want to check it with a JSON validator.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 1
    I thought when an *exception* is raised at this point it should more likely result in a 500 Internal Error response, isn't it? – iBug May 24 '20 at 08:23
  • 1
    @iBug It's an old question, but the answer is that it should result in a 400, because if the request wasn't malformed, the exception wouldn't be raised. – Mikael Aug 12 '20 at 15:52
  • 7
    @iBug: badly formed JSON in a request is not a server error, it is an error on the part of the client, making it a 400 class error. – Martijn Pieters Aug 12 '20 at 20:57
195

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: How to POST JSON data with 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)
Luke
  • 5,329
  • 2
  • 29
  • 34
93

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) # Do your processing
    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. If you are always expecting a json body (not optionally), leave it as silent=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.

starball
  • 20,030
  • 7
  • 43
  • 238
radtek
  • 34,210
  • 11
  • 144
  • 111
  • 4
    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 05 '16 at 18:39
  • 1
    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 05 '16 at 18:57
34

Assuming you've posted valid JSON with the application/json content type, request.json will have the parsed JSON data.

from flask import Flask, request, jsonify

app = Flask(__name__)


@app.route('/echo', methods=['POST'])
def hello():
   return jsonify(request.json)
davidism
  • 121,510
  • 29
  • 395
  • 339
trojek
  • 3,078
  • 2
  • 29
  • 52
  • 3
    To add to this answer the request you could send to this endpoint could be `response = request.post('http://127.0.0.1:5000/hello', json={"foo": "bar"})`. Following this running `response.json()` should return `{'foo': 'bar'}` – ScottMcC Jun 11 '17 at 08:54
16

For all those whose issue was from the ajax call, here is a full example :

Ajax call : the key here is to use a dict and then JSON.stringify

    var dict = {username : "username" , password:"password"};

    $.ajax({
        type: "POST", 
        url: "http://127.0.0.1:5000/", //localhost Flask
        data : JSON.stringify(dict),
        contentType: "application/json",
    });

And on server side :

from flask import Flask
from flask import request
import json

app = Flask(__name__)

@app.route("/",  methods = ['POST'])
def hello():
    print(request.get_json())
    return json.dumps({'success':True}), 200, {'ContentType':'application/json'} 

if __name__ == "__main__":
    app.run()
Arcyno
  • 4,153
  • 3
  • 34
  • 52
  • 1
    Note, for some reason it won't work if the dict is defined as const, e.g., `const foo = {hello: "world"}`. – niceman May 14 '21 at 15:23
11

If you use force=True, it will ignore the content type of the request and try to parse the body as JSON regardless.

request.get_json(force=True)
davidism
  • 121,510
  • 29
  • 395
  • 339
Dip
  • 119
  • 1
  • 3
11

You may note that request.json or request.get_json() works only when the Content-type: application/json has been added in the header of the request. If you are unable to change the client request configuration, so you can get the body as json like this:

data = json.loads(request.data)
Maicon Mauricio
  • 2,052
  • 1
  • 13
  • 29
Soroosh Khodami
  • 1,185
  • 9
  • 17
  • 1
    This worked for me, when trying to write google cloud function for dialogflow webhook as request is being sent as body in JSON format – jkr Jan 09 '21 at 13:42
5

To give another approach.

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

@app.route('/service', methods=['POST'])
def service():
    data = json.loads(request.data)
    text = data.get("text",None)
    if text is None:
        return jsonify({"message":"text not found"})
    else:
        return jsonify(data)

if __name__ == '__main__':
    app.run(host= '0.0.0.0',debug=True)
Ömer Taban
  • 130
  • 1
  • 5
2

The following codes can be used:

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

Here is a screenshot of me getting the json data:

enter image description here

You can see that what is returned is a dictionary type of data.

1

Assuming that you have posted valid JSON,

@app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
    content = request.json
    print content['uuid']
    # Return data as JSON
    return jsonify(content)
RAJAHMAD MULANI
  • 139
  • 1
  • 5
0

Even though all the answers I encounter here are right. There is something that I think it should be done as better practice. Here is how I would write it.

from flask import app, request, Flask, jsonify


@app.route('/api/add_message/<uuid>', methods=['GET', 'POST'])
def add_message(uuid):
    
    # Check if the request method is POST
    if request.method == 'POST':
        # content will return eather parse data as JSON
        # Or None incase there is no data
        content = request.get_json()
        print(content)
        # The content could be displayed in html page if serialized as json
        return jsonify(content) # Return null if there is content

    # if it is only get request then just return uuid
    return uuid
0
{
  "uuid":1212121212,
  "text":"lalala",
  "comment":"",
  "signed_on":"2022-11-07 00:03:00"
}

you can sent the above data as json and get it in flask application using request.json

from flask import request, Blueprint,flash,json

@app.route('/api/add_message/<uuid>', methods = ["GET", "POST"])
def testing(uuid):
    sync_data = request.json    
    josn_data = json.dumps(sync_data ,default =str)
    return josn_data
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Ganesan J
  • 539
  • 7
  • 12
-1

Try to set force attribute as True in get_json() method to resolve this issue.

request.get_json(force = True)
Codemaker2015
  • 12,190
  • 6
  • 97
  • 81