Flask is flexible enough that you can return value of various types from a view function.
backgroud
The official documentation has explained the return value of view functions as below
The logic that Flask applies to converting return values into response objects is as follows:
- If a response object of the correct type is returned it’s directly returned from the view.
- If it’s a string, a response object is created with that data and the default parameters.
- If a tuple is returned the items in the tuple can provide extra information. Such tuples have to be in the form (response, status, headers) or (response, headers) where at least one item has to be in the tuple. The status value will override the status code and headers can be a list or dictionary of additional header values.
- If none of that works, Flask will assume the return value is a valid WSGI application and convert that into a response object.
However it doesn’t cover all possible situations for example it's allowed to return (‘bad argument’, 400)
.
So this needs to be redefined in details.
After checking out the source code(0.13-dev) I am going to explain all available return forms with some examples. The core logic of processing return value is within make_response
which will convert the return value from a view function to an instance of response_class.
Examples below are based on this simple code snippet
from flask import Flask
app = Flask(__name__)
app.debug = True
@app.route('/')
def hello_world():
return 'Hello, World!'
def main():
app.run()
if __name__ == '__main__':
main()
return forms
string
what type is string?
- text type
- str(py3)
- unicode(py2)
- bytes
- bytearray
Please notice that str
equals bytes
in python2 and both represent the byte array after encoding.
Returning a string is the most common way in a view function for example:
@app.route('/string')
def string():
return 'hello world!\n'
render_template
returns a unicode/str string so it’s always used in the return statement:
@app.route('/template')
def template():
return render_template('index.html')
a response_class object
The default response_class is Response
.
You can construct a response object manually and do anything you want with it before return.
# Response
@app.route('/response')
def response():
response = Response('Hello world!\n', content_type='text/plain')
response.set_cookie('firstname', 'xurui')
response.set_cookie('lastname', 'yan')
return response
But quite often you don’t have to create this object yourself because make_response
will take care of that for you.
@app.route('/make_response')
def make_response():
response = flask.make_response(('Hello world!\n'), {'X-My-Header': 'foo'})
response.set_cookie('username', 'yxr')
return response
make_response
recognize arguments in all other possible forms discussed in this article.
Besides jsonify
will turn the json.dumps()
output to a Response with the application/json
mimetype so it is often used as the return value in API views.
@app.route('/jsonify')
def jsonify():
return flask.jsonify({'username': 'yxr'})
how to return chunked-encoded response
Return chunked response by passing a generator as the first argument when create Response instance.
@app.route('/chunked')
def chunked():
def gen():
yield 'Hello '
yield 'World!\n'
return Response(gen())
a WSGI app
The return value can be a callable WSGI app function, for example:
def simple_app(environ, start_response):
status = '200 OK'
response_headers = [('Content-Length', '13')]
start_response(status, response_headers)
return ['Hello World!\n']
@app.route('/wsgi')
def wsgi():
return simple_app
HTTPException
In addition, werkzeug.exceptions.HTTPException
can be called as WSGI application to render a default error page. So you can directly return such exceptions.
@app.route('/exception')
def exception():
# equivalent to raise NotFound
return NotFound()
raise NotFound
.
What’s the difference between return
and raise
? Which should I use?
The exception raised will be handled in handle_http_exception
.
If no handler is registered to handle this exception it will be return directly and finally processed by make_response. So the result is the same as return a HTTPException.
You are encouraged to use raise instead of return because of the 2 reasons below:
- return a exception seems strange
- raised exception can be handled by registered exception handler
abort
Another useful function is abort
which is used to abort a request early. Abort will raise a HTTPException with the corresponding status code set.
@app.route('/abort')
def test_abort():
abort(500, 'something is wrong!\n')
tuple or list
The return value can be a tuple or list.
3-tuple in (response, status, headers) form
@app.route('/tuple3')
def tuple3():
return 'hello world!\n', 200, {'X-My-Header': 'foo'}
2-tuple in (response, status) or (response, headers) form.
If the the second item is instance of Headers, dict, tuple or list, it is unpacked as the second form. status
can be int or any type that can be converted by int
constructor.
@app.route('/tuple2_status')
def tuple2_status():
return 'hello world!\n', 200
@app.route('/tuple2_header')
def tuple2_header():
return 'hello world!\n', {'X-My-Header': 'foo'}
If response is a string, a response_class object will be created by the string as showed by the examples above.
But response doesn’t have to be a string, a response_class object or WSGI app can also be accepted.
response is a response_class object
@app.route('/tuple2_response')
def tuple2_response():
response = flask.make_response(('hello world!\n'))
return response, {'X-My-Header':' foo'}
response is a callable WSGI app which is rarely used
@app.route('/tuple2_wsgi')
def tuple2_wsgi():
return simple_app, {'X-My-Header': 'foo'}
The status value will override the existing status code and headers will add additional headers.
@app.route('/tuple2_extend_header')
def tuple2_extend_header():
response = flask.make_response(('hello world!\n'), {'X-My-Header': 'foo'})
return response, {'X-My-Header':' bar'}
X-My-Header
.
None is not allowed
@app.route('/none')
def none():
# return nothing is equivalent to `return None`
pass
response is not allowed to be None even in tuple form
@app.route('/tuple_none')
def tuple_none():
return None, 404
TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement