Accessing incoming POST data in Flask

22,397

You're inadvertently sending the wrong Content Type.

By default, curl's -d flag will send POST data with content-type application/x-www-form-urlencoded. Since you're not sending data in the format that it's expecting (key=value), it's dropping the data altogether. For JSON data, you'll need to send the HTTP request with the content-type set to application/json like so:

curl -XPOST -H "Content-Type: application/json" 'http://127.0.0.1:5000/?thing1=1' -d '{"thing2":2}'

Also, flask's request.form field only contains POST form data, not other content types. You can access the raw POST request body with request.data or, more convenienty, the parsed JSON data using request.get_json.

Below is a fixed version of your example:

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

@app.route('/', methods=['GET', 'POST'])
def refresh():
    params = {
        'thing1': request.values.get('thing1'),
        'thing2': request.get_json().get('thing2')
    }
    return json.dumps(params)


app.run()

UPDATE: I misspoke earlier - request.body should actually be request.data. Also as it turns out request.json is deprecated and request.get_json should be used now instead. The original post has been updated.

Share:
22,397
Narfanator
Author by

Narfanator

Updated on July 30, 2022

Comments

  • Narfanator
    Narfanator almost 2 years

    Here's the flask code:

    from flask import Flask, request
    import json
    app = Flask(__name__)
    
    @app.route('/', methods=['GET', 'POST'])
    def refresh():
        params = {
            'thing1': request.values.get('thing1'),
            'thing2': request.values.get('thing2')
        }
        return json.dumps(params)
    

    Here's the cURL:

    $ curl -XGET 'http://127.0.0.1:5000/?thing1=1' -d '{"thing2":2}'                                              
    > {"thing1": "1", "thing2": null}
    
    $ curl -XGET 'http://127.0.0.1:5000/?thing1=1' -d '{"thing2":2}'                                              
    > {"thing1": "1", "thing2": null}
    

    The docs seem VERY clear that this should be working:

    form

    A MultiDict with the parsed form data from POST or PUT requests. Please keep in mind that file uploads will not end up here, but instead in the files attribute.

    args

    A MultiDict with the parsed contents of the query string. (The part in the URL after the question mark).

    values

    A CombinedMultiDict with the contents of both form and args.

    Any ideas what I'm doing wrong?

    Update: Trying suggestions from one of the answers, swapping out the return line:

    Using return json.dumps(json.load(request.body.decode("utf-8") )) generates the error AttributeError: 'Request' object has no attribute 'body'

    Using return json.dumps(json.load(request.json)) generates the error AttributeError: 'NoneType' object has no attribute 'read'

    Using POST with the origin code appears to have no effect:

    $ curl -XPOST 'http://127.0.0.1:5000/?thing1=1' -d '{"thing2":2}'
    {"thing1": "1", "thing2": null}
    

    Setting the content type and using POST with the original code also has no apparent effect:

    $ curl -XPOST -H "Content-Type: application/json" 'http://127.0.0.1:5000/?thing1=1' -d '{"thing2":2}' 
    {"thing1": "1", "thing2": null}
    

    although I went and verified that the content-type was then correctly set:

    ...
    print(request.headers)
    ...
    
    Host: 127.0.0.1:5000
    User-Agent: curl/7.54.0
    Accept: */*
    Content-Type: application/json
    Content-Length: 12