Flask - How do I read the raw body in a POST request when the content type is "application/x-www-form-urlencoded"
Asked Answered
G

7

23

Turns out that Flask sets request.data to an empty string if the content type of the request is application/x-www-form-urlencoded. Since I'm using a JSON body request, I just want to parse the json or force Flask to parse it and return request.json.

This is needed because changing the AJAX content type forces an HTTP OPTION request, which complicates the back-end.

How do I make Flask return the raw data in the request object?

Ghee answered 14/7, 2013 at 15:15 Comment(1)
Yep flask.request.data.decode("utf-8") blank, but I thought it was a client side problem. Thanks for confirming flask is doing this! Wow omg 10 years later! this conversation really saved my biscuit today ! I was puzzled for a few hours.Staid
E
20

You can get the post data via request.form.keys()[0] if content type is application/x-www-form-urlencoded.

request.form is a multidict, whose keys contain the parsed post data.

Excaudate answered 14/7, 2013 at 15:29 Comment(8)
It seems this was also the way to get the data our of a post to bottle. Just change request.form.keys()[0] to list(request.forms.keys())[0]. Thanks for finally solving hours of debugging!Lozenge
Why does it work? It seems it should be just the first key of the dictionary parsed from the body, but it somehow returns the entire body?Shorn
Can someone please explain why the data is put into the form keys?Carinacarinate
This does not give you all of the post data if the data contains = and & characters.Hawkinson
@Emil it should be form not forms.Hasen
Even though this works, I don't think it should be the accepted answer. The other two answers below offer much better approaches to code legibility and maintainability.Ides
2023. This is straight up wrong. The [0] doesn't reference to anything and gives to you a runtime error because form.keys() are in key: value pairs. param1=value1&param2=value2 returns ImmutableMultiDict([('key1', 'value1'), ('key2', 'value2')])Doughnut
I would use form.keys and go from there to get the data you need. get_data() returns a byte stream, only useful if you're dealing with binary data.`Doughnut
H
13

Use request.get_data() to get the POST data. This works independent of whether the data has content type application/x-www-form-urlencoded or application/octet-stream.

Hawkinson answered 1/9, 2017 at 15:44 Comment(2)
This doesn't seem to be the case. With the requests I'm getting, if I use get_data() I get an empty bytes(), but with the accepted answer's method (looking at the form keys) I get a bunch of data.Forsooth
Yup me too, nothing in get_data(), but it seems ridiculous to do it as in the accepted answer.Bruni
C
5

If you want get the JSON when request is 'Content-Type': 'application/x-www-form-urlencoded' you need "force" conversion to json like de code below:

from flask import Flask, request
import os


app = Flask(__name__)


@app.route("/my-endpoint", methods = ['POST'])
def myEndpoint():

    requestJson = request.get_json(force=True)

    //TODO: do something....

    return requestJson


if __name__ == "__main__":
    port = int(os.environ.get('PORT', 5000))
    app.run(host='0.0.0.0', port=port, debug=True, use_reloader=True)
Colloquium answered 9/3, 2017 at 14:51 Comment(1)
Probably it is outdated now so it's not workingBraga
F
1

main.py

import json

from flask import Flask, request

app = Flask(__name__)


@app.route('/', methods=['POST'])
def index():
    mimetype = request.mimetype
    if mimetype == 'application/x-www-form-urlencoded':
        form = json.loads(next(iter(request.form.keys())))
    elif mimetype == 'multipart/form-data':
        form = dict(request.form)
    elif mimetype == 'application/json':
        form = request.json
    else:
        form = request.data.decode()
    print(mimetype, form, type(form))
    return form


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

Test

curl -X POST http://127.0.0.1:5000/ --data "{\"a\":1, \"b\":2}"

curl -X POST http://127.0.0.1:5000/ -F a=1 -F b=2

curl -X POST -H "Content-type: application/json" http://127.0.0.1:5000/ --data "{\"a\":1, \"b\":2}"

Result

application/x-www-form-urlencoded {'a': 1, 'b': 2} <class 'dict'>
multipart/form-data {'a': '1', 'b': '2'} <class 'dict'>
application/json {'a': 1, 'b': 2} <class 'dict'>
Fain answered 30/6, 2022 at 14:10 Comment(0)
K
1

You can get it that way:

@app.route("/path-to-the-post-endpoint", methods=["POST"])
def handle_post_request():
    data = request.form.to_dict()
    data['some_key_1'] = "Some Value 1"
    data['some_key_2'] = "Some Value 2"
    # ...etc.

    # DO SOMETHING HERE

    return [], 200
Krebs answered 6/2, 2023 at 12:54 Comment(1)
My post request Content-Type was application/x-www-form-urlencoded and I want to convert it to a dictionary. This worked for me request.form.to_dict()Maynardmayne
H
0

try this:

f = request.form

   output = []

   user_data = {}

   user_data['email'] = f['email']

   user_data['password'] = f['password']

   user_data['key'] = f['key']

   output.append(user_data)
Homicide answered 6/12, 2019 at 9:16 Comment(0)
D
0

request.values will obtain both args and post, proiritizing args per Python docs

request.json will handle a JSON payload.

Doughnut answered 5/7, 2023 at 7:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.