Python Flask-Restful POST not taking JSON arguments

70,501

Solution 1

According to the documentation for Request.json and the new Request.get_json, you should have the mimetype on your POST request set to application/json. This is the only way flask will automatically parse your JSON data into the Request.json property which (I believe) is what Flask-Restful depends on to retrieve JSON data.

NOTE: The newer get_json function has an option to force the parsing of POST data as JSON irrespective of the mimetype

Solution 2

junnytony's answer gave me a hint, and I went ahead with this approach. get_json seems to have done the trick.

from flask import Flask, jsonify, request
from flask_restful import reqparse, abort, Api, Resource

app = Flask(__name__)
api = Api(app)

#parser = reqparse.RequestParser()
#parser.add_argument('username', type=unicode, location='json')
#parser.add_argument('password', type=unicode, location='json')

class HelloWorld(Resource):
    def post(self):
        json_data = request.get_json(force=True)
        un = json_data['username']
        pw = json_data['password']
        #args = parser.parse_args()
        #un = str(args['username'])
        #pw = str(args['password'])
        return jsonify(u=un, p=pw)

api.add_resource(HelloWorld, '/testing')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5444 ,debug=True)

Solution 3

After forcing the request to parse json, it worked with me. Here is the code:

from flask import Flask, jsonify, request
from flask_restful import reqparse, abort, Api, Resource

app = Flask(__name__)
api = Api(app)

parser = reqparse.RequestParser()
parser.add_argument('username', type=str)
parser.add_argument('password', type=str)

class HelloWorld(Resource):
    def post(self):
        request.get_json(force=True)
        args = parser.parse_args()
        un = str(args['username'])
        pw = str(args['password'])
        return jsonify(u=un, p=pw)

api.add_resource(HelloWorld, '/testing')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5444 ,debug=True)

Solution 4

I ran into a similar issue and here is a solution that works for me. let's say your application looks like this:

from flask import Flask, jsonify
from flask_restful import Api, Resource, reqparse

app = Flask(__name__)
api = Api(app)

# Define parser and request args
parser = reqparse.RequestParser()
parser.add_argument('last_name', type=str)
parser.add_argument('first_name', type=str)
# not the type=dict
parser.add_argument('personal_data', type=dict)


class Item(Resource):

    def post(self):

        args = parser.parse_args()

        ln = args['last_name']
        fn = args['first_name']
        # we can also easily parse nested structures
        age = args['personal_data']['age']
        nn = args['personal_data']['nicknames']

        return jsonify(fn=fn, ln=ln, age=age, nn=nn)


api.add_resource(Item, '/item')

if __name__ == '__main__':
    app.run(debug=True)

Now, you can easily create some JSON data:

import json

d = {'last_name': 'smith', 'first_name': 'john', 'personal_data': {'age': 18, 'height': 180, 'nicknames': ['johnny', 'grandmaster']}}

print(json.dumps(d, indent=4))

{
    "last_name": "smith",
    "first_name": "john",
    "personal_data": {
        "age": 18,
        "height": 180,
        "nicknames": [
            "johnny",
            "grandmaster"
        ]
    }
}

json.dumps(d)
'{"last_name": "smith", "first_name": "john", "personal_data": {"age": 18, "height": 180, "nicknames": ["johnny", "grandmaster"]}}'

and call the application:

curl http://localhost:5000/item -d '{"last_name": "smith", "first_name": "john", "personal_data": {"age": 18, "height": 180, "nicknames": ["johnny", "grandmaster"]}}'

This will crash with the error (I shortened the traceback):

age = args['personal_data']['age']
TypeError: 'NoneType' object is not subscriptable

the reason is that the header is not specified. If we add the

-H "Content-Type: application/json"

and then call

curl http://localhost:5000/item -H "Content-Type: application/json" -d '{"last_name": "smith", "first_name": "john", "personal_data": {"age": 18, "height": 180, "nicknames": ["johnny", "grandmaster"]}}'

The output looks as expected:

{
  "age": 18, 
  "fn": "john", 
  "ln": "smith", 
  "nn": [
    "johnny", 
    "grandmaster"
  ]
}

The function can be also further simplified to:

class Item(Resource):

    def post(self):

        json_data = request.get_json()
        # create your response below

as shown above.

Share:
70,501
sudhishkr
Author by

sudhishkr

Updated on May 23, 2020

Comments

  • sudhishkr
    sudhishkr almost 4 years

    I am very new to Flask (& Flask-Restful).

    My problem : json arguments for a POST is getting set to NONE (not working).

    I am able to take arguments from the form-data, using POSTMAN plugin for chrome. But, when i switch to raw (& feed a json), it fails to read the json & assigns a NONE to all my arguments.

    I have read some related stackoverflow posts related to this : link1, link2, link3 ... none of these helped me.

    I am using python-2.6, Flask-Restful-0.3.3, Flask-0.10.1, Chrome, POSTMAN on Oracle Linux 6.5.

    Python code app.py :

    from flask import Flask, jsonify
    from flask_restful import reqparse, abort, Api, Resource
    
    app = Flask(__name__)
    api = Api(app)
    
    parser = reqparse.RequestParser()
    parser.add_argument('username', type=str)
    parser.add_argument('password', type=str)
    
    class HelloWorld(Resource):
        def post(self):
            args = parser.parse_args()
            un = str(args['username'])
            pw = str(args['password'])
            return jsonify(u=un, p=pw)
    
    api.add_resource(HelloWorld, '/testing')
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=5444 ,debug=True)
    

    Testing this using POSTMAN :

    • Using form-data : works perfectly !
    • Using raw -> json : causes this issue

    Things tried #1 :

    Add json parameter to my add_argument() method in app.py

    parser = reqparse.RequestParser()
    parser.add_argument('username', type=str, location='json') # added json
    parser.add_argument('password', type=str, location='json') # added json
    

    Input : { "username": "hello", "password": "world" }

    Output : { "p": "None", "u": "None" }

    Things tried #2 :

    Change type to unicode in add_argument() method in app.py

    parser = reqparse.RequestParser()
    parser.add_argument('username', type=unicode, location='json') # change type to unicode
    parser.add_argument('password', type=unicode, location='json') # change type to unicode
    

    Input : { "username": "hello", "password": "world" }

    Output : { "p": "None", "u": "None" }


    PS : I will keep updating my question, with every failed attempt. Please let me know if you need any more info to make this question more clear.

    • junnytony
      junnytony almost 9 years
      Looks to me like you're missing a call to parser.parse_args(). You should have args = parser.parse_args() before you can access the args
    • sudhishkr
      sudhishkr almost 9 years
      @junnytony - its a typo from my side, i forgot to copy that line. Editing the question right away!
    • sirfz
      sirfz almost 9 years
      Try unicode instead of str for the arguments types
    • sudhishkr
      sudhishkr almost 9 years
      @Sir_FZ unicode has the same issue, retains None
  • junnytony
    junnytony almost 9 years
    Nice :). but I think your code would be even more awesome if you changed the mimetype for your post to be 'application/json'. I'm guessing you're using AJAX for your post. With something like jQuery you can easily add the appropriate mimetype for example stackoverflow.com/questions/6323338/…
  • Brian Wylie
    Brian Wylie almost 9 years
    Thanks for providing your final code, I have a similar use case.
  • KCN
    KCN almost 7 years
    HI, I am also facing the same issue.As per your suggestion I done but still getting bad request error
  • sudhishkr
    sudhishkr almost 7 years
    what your exact issue @KCN?
  • KCN
    KCN almost 7 years
    As per your suggestion I followed like json_dat = request.get_json(force=True) fname = json_dat["firstname"] lname = json_dat["lastname"] but while executing this url from rest client getting 400 bad request error