Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing incoming POST data in Flask

Tags:

python

curl

flask

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
like image 550
Narfanator Avatar asked Jan 16 '18 02:01

Narfanator


1 Answers

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.

like image 50
jtimmons Avatar answered Sep 19 '22 14:09

jtimmons