7. Accessing Request Data¶
For web applications it’s crucial to react to the data a client sends to
the server. In Flask this information is provided by the global
request
object. If you have some experience with Python
you might be wondering how that object can be global and how Flask
manages to still be threadsafe. The answer is context locals:
7.1. Context Locals¶
Note
Insider Information
If you want to understand how that works and how you can implement tests with context locals, read this section, otherwise just skip it.
Certain objects in Flask are global objects, but not of the usual kind. These objects are actually proxies to objects that are local to a specific context. What a mouthful. But that is actually quite easy to understand.
Imagine the context being the handling thread. A request comes in and the web server decides to spawn a new thread (or something else, the underlying object is capable of dealing with concurrency systems other than threads). When Flask starts its internal request handling it figures out that the current thread is the active context and binds the current application and the WSGI environments to that context (thread). It does that in an intelligent way so that one application can invoke another application without breaking.
So what does this mean to you? Basically you can completely ignore that
this is the case unless you are doing something like unit testing. You
will notice that code which depends on a request object will suddenly break
because there is no request object. The solution is creating a request
object yourself and binding it to the context. The easiest solution for
unit testing is to use the test_request_context()
context manager. In combination with the with
statement it will bind a
test request so that you can interact with it. Here is an example:
from flask import request
with app.test_request_context('/hello', method='POST'):
# now you can do something with the request until the
# end of the with block, such as basic assertions:
assert request.path == '/hello'
assert request.method == 'POST'
The other possibility is passing a whole WSGI environment to the
request_context()
method:
from flask import request
with app.request_context(environ):
assert request.method == 'POST'
7.2. The Request Object¶
The request object is documented in the API section and we will not cover
it here in detail (see request
). Here is a broad overview of
some of the most common operations. First of all you have to import it from
the flask
module:
from flask import request
The current request method is available by using the
method
attribute. To access form data (data
transmitted in a POST
or PUT
request) you can use the
form
attribute. Here is a full example of the two
attributes mentioned above:
@app.route('/login', methods=['POST', 'GET'])
def login():
error = None
if request.method == 'POST':
if valid_login(request.form['username'],
request.form['password']):
return log_the_user_in(request.form['username'])
else:
error = 'Invalid username/password'
# the code below is executed if the request method
# was GET or the credentials were invalid
return render_template('login.html', error=error)
What happens if the key does not exist in the form
attribute? In that
case a special KeyError
is raised. You can catch it like a
standard KeyError
but if you don’t do that, a HTTP 400 Bad Request
error page is shown instead. So for many situations you don’t have to
deal with that problem.
To access parameters submitted in the URL (?key=value
) you can use the
args
attribute:
searchword = request.args.get('key', '')
We recommend accessing URL parameters with get or by catching the
KeyError
because users might change the URL and presenting them a 400
bad request page in that case is not user friendly.
For a full list of methods and attributes of the request object, head over
to the request
documentation.
7.3. File Uploads¶
You can handle uploaded files with Flask easily. Just make sure not to
forget to set the enctype="multipart/form-data"
attribute on your HTML
form, otherwise the browser will not transmit your files at all.
Uploaded files are stored in memory or at a temporary location on the
filesystem. You can access those files by looking at the
files
attribute on the request object. Each
uploaded file is stored in that dictionary. It behaves just like a
standard Python file
object, but it also has a
save()
method that allows you to store that
file on the filesystem of the server. Here is a simple example showing how
that works:
from flask import request
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/uploaded_file.txt')
...
If you want to know how the file was named on the client before it was
uploaded to your application, you can access the
filename
attribute. However please keep in
mind that this value can be forged so never ever trust that value. If you
want to use the filename of the client to store the file on the server,
pass it through the secure_filename()
function that
Werkzeug provides for you:
from flask import request
from werkzeug.utils import secure_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/' + secure_filename(f.filename))
...
For some better examples, checkout the Uploading Files pattern.
7.4. Cookies¶
To access cookies you can use the cookies
attribute. To set cookies you can use the
set_cookie
method of response objects. The
cookies
attribute of request objects is a
dictionary with all the cookies the client transmits. If you want to use
sessions, do not use the cookies directly but instead use the
Sessions in Flask that add some security on top of cookies for you.
Reading cookies:
from flask import request
@app.route('/')
def index():
username = request.cookies.get('username')
# use cookies.get(key) instead of cookies[key] to not get a
# KeyError if the cookie is missing.
Storing cookies:
from flask import make_response
@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp
Note that cookies are set on response objects. Since you normally
just return strings from the view functions Flask will convert them into
response objects for you. If you explicitly want to do that you can use
the make_response()
function and then modify it.
Sometimes you might want to set a cookie at a point where the response object does not exist yet. This is possible by utilizing the Deferred Request Callbacks pattern.
For this also see About Responses.