Flask: return value of view function

Published On July 27, 2017

category python | tags flask


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:

  1. If a response object of the correct type is returned it’s directly returned from the view.
  2. If it’s a string, a response object is created with that data and the default parameters.
  3. 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.
  4. 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()
This is equivalent to 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:

  1. return a exception seems strange
  2. 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'}
This will result in a duplicated header 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
you will see error like this

TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement

reference


qq email facebook github
© 2018 - Xurui Yan. All rights reserved
Built using pelican