Categories:Viewed: 60 - Published at: 6 months ago

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 or request.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!

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.

Reference: stackabuse.com

TAGS :