Processing Incoming Request Data in Flask

By Anthony Herbert

In any web app, you’ll have to process incoming request data from users. Flask, like any other web framework, allows you to access the request data easily.

In this tutorial, we’ll go through how to process incoming data for the most common use cases. The forms of incoming data we’ll cover are: query strings, form data, and JSON objects. To demonstrate these cases, we’ll build a simple example app with three routes that accept either query string data, form data, or JSON data.

The Request Object

To access the incoming data in Flask, you have to use the request object. The request object holds all incoming data from the request, which includes the mimetype, referrer, IP address, raw data, HTTP method, and headers, among other things. Although all the information the request object holds can be useful, in this article, we’ll only focus on the data that is normally directly supplied by the caller of our endpoint.

To gain access to the request object in Flask, you simply import it from the Flask library.

from flask import request

You then have the ability to use it in any of your view functions.

Once we get to the section on query strings, you’ll get to see the request object in action.

The Example App

To demonstrate the different ways of using request, we’ll start with a simple Flask app. Even though the example app uses a simple organization layout for the view functions and routes, what you learn in this tutorial can be applied to any method of organizing your views like class-based views, blueprints, or an extension like Flask-Via.

To get started, first we need to install Flask.

pip install Flask

Then, we can start with the following code.

#app.py

from flask import Flask, request #import main Flask class and request object

app = Flask(__name__) #create the Flask app

@app.route('/query-example')
def query_example():
    return 'Todo...'

@app.route('/form-example')
def form_example():
    return 'Todo...'

@app.route('/json-example')
def json_example():
    return 'Todo...'

if __name__ == '__main__':
    app.run(debug=True, port=5000) #run app in debug mode on port 5000

Start the app with:

python app.py

The code supplied sets up three routes with a message telling us ‘Todo…’. The app will start on port 5000, so you can view each route in your browser with the following links:

http://127.0.0.1:5000/query-example (or localhost:5000/query-example)
http://127.0.0.1:5000/form-example (or localhost:5000/form-example)
http://127.0.0.1:5000/json-example (or localhost:5000/json-example)

For each of the three routes, you’ll see the same thing:

Query Arguments

URL arguments that you add to a query string are the simpliest way to pass data to a web app, so let’s start with them.

A query string looks like the following:

example.com?arg1=value1&arg2=value2

The query string begins after the question mark (?) and has two key-value pairs separated by an ampersand (&). For each pair, the key is followed by an equals sign (=) and then the value. Even if you’ve never heard of a query string until now, you have definitely seen them all over the web.

So in that example, the app receives:

arg1 : value1
arg2 : value2

Query strings are useful for passing data that doesn’t require the user to take action. You could generate a query string somewhere in your app and append it a URL so when a user makes a request, the data is automatically passed for them. A query string can also be generated by forms that have GET as the method.

To create our own query string on the query-example route, we’ll start with this simple one:

http://127.0.0.1:5000/query-example?language=Python

If you run the app and navigate to that URL, you’ll see that nothing has changed. That’s only because we haven’t handled the query arguments yet.

To do so, we’ll need to read in the language key by using either request.args.get('language') or request.args['language'].

By calling request.args.get('language'), our application will continue to run if the language key doesn’t exist in the URL. In that case, the result of the method will be None. If we use request.args['language'], the app will return a 400 error if language key doesn’t exist in the URL. For query strings, I recommend using request.args.get() because of how easy it is for the user to modify the URL. If they remove one of the keys, request.args.get() will prevent the app from failing.

Let’s read the language key and display it as output. Modify with query-example route the following code.

@app.route('/query-example')
def query_example():
    language = request.args.get('language') #if key doesn't exist, returns None

    return '''<h1>The language value is: {}</h1>'''.format(language)

Then run the app and navigate to the URL.

As you can see, the argument from the URL gets assigned to the language variable and then gets returned to the browser. In a real app, you’d probably want to do something with the data other than simply return it.

To add more query string parameters, we just append ampersands and the new key-value pairs to the end of the URL. So an additional pair would look like this:

http://127.0.0.1:5000/query-example?language=Python&framework=Flask

With the new pair being:

framework : Flask

And if you want more, continue adding ampersands and key-value pairs. Here’s an example:

http://127.0.0.1:5000/query-example?language=Python&framework=Flask&website=Scotch

To gain access to those values, we still use either request.args.get() or request.args[]. Let’s use both to demonstrate what happens when there’s a missing key. We’ll assign the value of the results to variables and then display them.

@app.route('/query-example')
def query_example():
    language = request.args.get('language') #if key doesn't exist, returns None
    framework = request.args['framework'] #if key doesn't exist, returns a 400, bad request error
    website = request.args.get('website')

    return '''<h1>The language value is: {}</h1>
              <h1>The framework value is: {}</h1>
              <h1>The website value is: {}'''.format(language, framework, website)

When you run that, you should see:

If you remove the language from the URL, then you’ll see that the value ends up as None.

If you remove the framework key, you get an error.

Now that you undertand query strings, let’s move on to the next type of incoming data.

Form Data

Next we have form data. Form data comes from a form that has been sent as a POST request to a route. So instead of seeing the data in the URL (except for cases when the form is submitted with a GET request), the form data will be passed to the app behind the scenes. Even though you can’t easily see the form data that gets passed, your app can still read it.

To demonstrate this, modify the form-example route to accept both GET and POST requests and to return a simple form.

@app.route('/form-example', methods=['GET', 'POST']) #allow both GET and POST requests
def form_example():
    return '''<form method="POST">
                  Language: <input type="text" name="language"><br>
                  Framework: <input type="text" name="framework"><br>
                  <input type="submit" value="Submit"><br>
              </form>'''

Running the app results in this:

The most important thing to know about this form is that it performs a POST request to the same route that generated the form. The keys that we’ll read in our app all come from the “name” attributes on our form inputs. In our case, language and framework are the names of the inputs, so we’ll have access to those in our app.

Inside the view function, we need to check if the request method is GET or POST. If it’s GET, we simply display the form we have. If it’s POST, then we want to process the incoming data.

To do that, let’s add a simple if statement that checks for a POST request. If the request method isn’t POST, then we know it’s GET, because our route only allows those two types of requests. For GET requests, the form will be generated.

@app.route('/form-example', methods=['GET', 'POST']) #allow both GET and POST requests
def form_example():
    if request.method == 'POST': #this block is only entered when the form is submitted
        return 'Submitted form.'

    return '''<form method="POST">
                  Language: <input type="text" name="language"><br>
                  Framework: <input type="text" name="framework"><br>
                  <input type="submit" value="Submit"><br>
              </form>'''

Inside of the block, we’ll read the incoming values with request.args.get('language') and request.form['framework']. Remember that if we have more inputs, then we can read additional data.

Add the additional code to the form-example route.

@app.route('/form-example', methods=['GET', 'POST']) #allow both GET and POST requests
def form_example():
    if request.method == 'POST':  #this block is only entered when the form is submitted
        language = request.form.get('language')
        framework = request.form['framework']

        return '''<h1>The language value is: {}</h1>
                  <h1>The framework value is: {}</h1>'''.format(language, framework)

    return '''<form method="POST">
                  Language: <input type="text" name="language"><br>
                  Framework: <input type="text" name="framework"><br>
                  <input type="submit" value="Submit"><br>
              </form>'''

Try running the app and submitting the form.

Similiar to the query string example before, we can use request.form.get() instead of referencing the key directly with request.form[]. request.form.get() returns None instead of causing a 400 error when the key isn’t found.

As you can see, handling submitted form data is just as easy as handling query string arguments.

JSON Data

Finally, we have JSON data. Like form data, it’s not so easy to see. JSON data is normally constructed by a process that calls our route. An example JSON object looks like this:

{
    "language" : "Python",
    "framework" : "Flask",
    "website" : "Scotch",
    "version_info" : {
        "python" : 3.4,
        "flask" : 0.12
    },
    "examples" : ["query", "form", "json"],
    "boolean_test" : true
}

As you can see with JSON, you can pass much more complicated data that you could with query strings or form data. In the example, you see nested JSON objects and an array of items. With Flask, reading all of these values is straightforward.

First, to send a JSON object, we’ll need a program capable of sending custom requests to URLs. For this, we can use an app called Postman.

Before we use Postman though, change the method on the route to accept only POST requests.

@app.route('/json-example', methods=['POST']) #GET requests will be blocked
def json_example():
    return 'Todo...'

Then in Postman, let’s do a little set up to enable sending POST requests.

In Postman, add the URL and change the type to POST. On the body tab, change to raw and select JSON (application/json) from the drop down. All this is done so Postman can send JSON data properly and so your Flask app will understand that it’s receiving JSON.

From there, you can copy the example into the text input. It should look like this:

To test, just send the request and you should get ‘Todo…’ as the response because we haven’t modified our function yet.

To read the data, first you must understand how Flask translates JSON data into Python data structures.

Anything that is an object gets converted to a Python dict. {"key" : "value"} in JSON corresponds to somedict['key'], which returns a value in Python.

An array in JSON gets converted to a list in Python. Since the syntax is the same, here’s an example list: [1,2,3,4,5]

Then the values inside of quotes in the JSON object become strings in Python. true and false become True and False in Python. Finally, numbers without quotes around them become numbers in Python.

Now let’s get on to reading the incoming JSON data.

First, let’s assign everything from the JSON object into a variable using request.get_json().

req_data = request.get_json()

request.get_json() converts the JSON object into Python data for us. Let’s assign the incoming request data to variables and return them by making the following changes to our json-example route.

@app.route('/json-example', methods=['POST']) #GET requests will be blocked
def json_example():
    req_data = request.get_json()

    language = req_data['language']
    framework = req_data['framework']
    python_version = req_data['version_info']['python'] #two keys are needed because of the nested object
    example = req_data['examples'][0] #an index is needed because of the array
    boolean_test = req_data['boolean_test']

    return '''
           The language value is: {}
           The framework value is: {}
           The Python version is: {}
           The item at index 0 in the example list is: {}
           The boolean value is: {}'''.format(language, framework, python_version, example, boolean_test)

If we run our app and submit the same request using Postman, we will get this:

Note how you access elements that aren’t at the top level. ['version']['python'] is used because you are entering a nested object. And ['examples'][0] is used to access the 0th index in the examples array.

If the JSON object sent with the request doesn’t have a key that is accessed in your view function, then the request will fail. If you don’t want it to fail when a key doesn’t exist, you’ll have to check if the key exists before trying to access it. Here’s an example:

language = None
if 'language' in req_data:
    language = req_data['language']

Conclusion

You should now understand how to use the request object in Flask to get the most common forms of input data. To recap, we covered:

  • Query Strings
  • Form Data
  • JSON Data

With this knowledge, you’ll have no problem with any data that users throw at your app!

Learn More

If you like this tutorial and want to learn more about Flask from me, check out my Flask for Beginners video course at my site Pretty Printed, which takes you from knowing nothing about Flask to building three apps. If that course isn’t at your level, then I have other courses on my site that may interest you as well.

Source:: scotch.io