Introduction
Flask is a great micro-framework for Web Development in Python, and allows you to be extremely minimal. A working REST API can be served up in seconds through a few lines of code:
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello!'
if __name__ == "__main__":
app.run()
The backbone of the modern web is the HTTP protocol - which sends requests and delivers responses back. To differentiate the intentions behind these requests, several "verbs" have been associated with tasks you're performing. GET
verbs are used to annotate requests in which you wish to retrieve resources, POST
verbs are used to request resources to be created, given the payload (body), DELETE
verbs are used to request resource deletion, etc.
If you wish to create a resource on a server - you'll be sending a POST Request with a body that contains the data you're posting to the server.
In this guide, we'll take a look at how to get an HTTP POST Body in Flask.
In general, you'll most likely be posting JSON data to a REST API that consumes that data, or you'll be posting Form data - by having a user fill out a web form, and then sending that data to another API for processing.
When sending form data - it's typically encoded as multipart/form-data
, while when sending JSON data - it's typically encoded as application/json
. This information is embedded in the POST Request Header which you can, also check. For good measure - we'll be checking the request's headers before parsing the data.
When dealing with requests - the request
module of flask
allows you to represent incoming HTTP requests. A POST request's body can be extracted directly from the request itself and depending on the encoding - you'll access the appropriate field:
request.json
orrequest.get_json()
request.form
request.data
request.json
represents JSON sent as a request with the application/json
content-type. Alternatively, you can use the request.get_json()
method. Both accessing the field itself and the method returns a dict
- with key-value
pairs present in the incoming JSON.
Note: The json
field and get_json()
methods will only work if the Content-Type of the POST request is set to application/json
. If it's a JSON-formatted string - this approach will fail and result in a None
value. If you can't enforce the client to send properly encoded data - you can convert the incoming string into JSON. Covered later in the guide.
request.form
represents the multipart/form-data
data that is acquired through web forms.
request.data
is a string representation of the incoming data. In general - you'll use this representation to convert into JSON if you can't force the client to send the content-type you're expecting.
Get POST JSON
Let's start out with JSON - since this is the most commonly used format to transfer data between APIs. We'll create a simple route handler that receives a POST
request, on the /post_json
endpoint. Remember, the json
field will only ever contain a value if the headers of the request properly annotate the body as an application/json
payload.
Here, we'll also get the 'Content-Type' from the headers
and check if the body indeed is application/json
formatted. If not - we won't even try extracting JSON from the request (it will silently fail if we do) and an error message is returned:
from flask import Flask, request
# ...
@app.route('/post_json', methods=['POST'])
def process_json():
content_type = request.headers.get('Content-Type')
if (content_type == 'application/json'):
json = request.json
return json
else:
return 'Content-Type not supported!'
If you send a POST request to your endpoint now - you'll be greeted with the JSON returned back:
$ curl -X POST -H "Content-type: application/json" -d "{\"firstName\" : \"John\", \"lastName\" : \"Smith\"}" "localhost:5000/post_json"
Note: Depending on the operating system and shell you're using - you might use '
instead of "
or even skip the escape characters such as \
altogether.
This results in:
{"firstName":"John","lastName":"Smith"}
Awesome! Let's try setting the -H
argument to another type - to check if the validation step works well:
$ curl -X POST -H "Content-type: multipart/form-data" -d "{\"firstName\" : \"John\", \"lastName\" : \"Smith\"}" "localhost:5000/post_json"
This results in:
Content-Type not supported!
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
Alternatively, get_json()
works in much the same way:
from flask import Flask, request
# ...
@app.route('/post_json', methods=['POST'])
def process_json():
content_type = request.headers.get('Content-Type')
if (content_type == 'application/json'):
json = request.get_json()
return json
else:
return 'Content-Type not supported!'
Get POST JSON from String
We were the ones sending the request so far - so we had the liberty of changing the content-type as we saw fit. This might not always be the case - and sometimes, you can run into a JSON-formatted request that doesn't have the correct content-type assigned to it.
In that case - json
and get_json()
don't parse the incoming body as JSON at all - and will end up being None
, out of which you can't extract anything. In such cases - you can use the json
module to load the string you've received into a dictionary (key-value
pairs)!
Let's import the module and convert the incoming request.data
:
from flask import Flask, request, json
# ...
@app.route('/post_json', methods=['POST'])
def process_json():
data = json.loads(request.data)
return data
Now - whether you send a text/plain
-encoded body, or an application/json
-encoded body - the json
module can handle the input. If we try sending either of these requests - they'd both result in the same response:
$ curl -X POST -H "Content-type: application/json" -d "{\"firstName\" : \"John\", \"lastName\" : \"Smith\"}" "localhost:5000/post_json"
$ curl -X POST -H "Content-type: text/plain" -d "{\"firstName\" : \"John\", \"lastName\" : \"Smith\"}" "localhost:5000/post_json"
They result in:
{"firstName":"John","lastName":"Smith"}
Get POST Form
When filling out forms - you have a series of inputs, and their corresponding labels. Under the hood - these are all just key-value
pairs:
username=user_input
password=user_input_2
...
This is typically derived from the front-end - usually an HTML page with a <form>
tag with several <input>
fields within it. You can also send form data via curl
as:
$ curl -X POST -H "Content-type: multipart/form-data" -F "username=john" -F "password=doe" "localhost:5000/post_form"
Or, you can collapse all of the fields into a single argument:
$ curl -X POST -H "Content-type: multipart/form-data" -F "username=john&password=doe" "localhost:5000/post_form"
In general - you'll be working with actual forms though, and a form we would be working with looks like:
<form action="/post_form" enctype="multipart/form-data" method="POST">
<input type="text" name="username">
<input type="password" name="password">
</form>
The name
of each <input>
is mapped as the key
to a value
input by the user. The form
extracted from the request
object is yet another dict
- and you can access the fields individually easily:
from flask import Flask, request
# ...
@app.route('/post_form', methods=['POST'])
def process_form():
data = request.form
print(data['username'])
print(data['password'])
return data
When we send a request via the console - the dictionary containing the key-value pairs is returned (which is then formatted again, as JSON):
{"password":"doe","username":"john"}
And on the server-end - the inputs for these two fields have been printed, right beneath the log for the incoming request
127.0.0.1 - - [09/Dec/2021 00:24:32] "POST /post_form HTTP/1.1" 200 -
john
doe
Naturally, instead of just printing the values to the console - you would validate the data, create a user, and persist them in the database. Alternatively, you can populate the fields of any class this way, before using that class for its purpose.
Conclusion
In this guide, we've taken a look at how to handle incoming HTTP POST requests in Flask. We've covered incoming JSON data, as well as how to handle string-represented JSON which isn't picked up automatically.
Finally, we've covered form data.