Allow dictionary return values as JSON

This supports an increasingly common usecase whereby JSON is the
primary response (rather than a templated string). Given Flask has a
short syntax for HTML reponses, it seems fitting that it should also
do so for JSON responses. In practice it allows,

     @app.route("/")
     def index():
         return {
             "api_stuff": "values",
         }
This commit is contained in:
pgjones 2019-03-03 17:34:29 +00:00 committed by David Lord
parent 2616d97f32
commit 7bf8366970
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
4 changed files with 37 additions and 16 deletions

View File

@ -52,6 +52,10 @@ Unreleased
- Add an ``--extra-files`` option to the ``flask run`` CLI command to - Add an ``--extra-files`` option to the ``flask run`` CLI command to
specify extra files that will trigger the reloader on change. specify extra files that will trigger the reloader on change.
:issue:`2897` :issue:`2897`
- Allow returning a dictionary from a view function. Similar to how
returning a string will produce a ``text/html`` response, returning
a dict will call ``jsonify`` to produce a ``application/json``
response. :pr:`3111`
.. _#2935: https://github.com/pallets/flask/issues/2935 .. _#2935: https://github.com/pallets/flask/issues/2935
.. _#2957: https://github.com/pallets/flask/issues/2957 .. _#2957: https://github.com/pallets/flask/issues/2957

View File

@ -679,23 +679,26 @@ See :ref:`error-handlers` for more details.
About Responses About Responses
--------------- ---------------
The return value from a view function is automatically converted into a The return value from a view function is automatically converted into
response object for you. If the return value is a string it's converted a response object for you. If the return value is a string it's
into a response object with the string as response body, a ``200 OK`` converted into a response object with the string as response body, a
status code and a :mimetype:`text/html` mimetype. ``200 OK`` status code and a :mimetype:`text/html` mimetype. If the
The logic that Flask applies to converting return values into return value is a dict, ``jsonify`` is called to produce a response.
response objects is as follows: 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 1. If a response object of the correct type is returned it's directly
returned from the view. returned from the view.
2. If it's a string, a response object is created with that data and the 2. If it's a string, a response object is created with that data and
default parameters. the default parameters.
3. If a tuple is returned the items in the tuple can provide extra information. 3. If it's a dict, a response object is created using ``jsonify``.
Such tuples have to be in the form ``(response, status, headers)``, 4. If a tuple is returned the items in the tuple can provide extra
``(response, headers)`` or ``(response, status)`` where at least one item information. Such tuples have to be in the form
has to be in the tuple. The ``status`` value will override the status code ``(response, status)``, ``(response, headers)``, or
and ``headers`` can be a list or dictionary of additional header values. ``(response, status, headers)``. The ``status`` value will override
4. If none of that works, Flask will assume the return value is a the status code and ``headers`` can be a list or dictionary of
additional header values.
5. If none of that works, Flask will assume the return value is a
valid WSGI application and convert that into a response object. valid WSGI application and convert that into a response object.
If you want to get hold of the resulting response object inside the view If you want to get hold of the resulting response object inside the view

View File

@ -44,6 +44,7 @@ from .helpers import (
url_for, url_for,
get_load_dotenv, get_load_dotenv,
) )
from .json import jsonify
from .logging import create_logger from .logging import create_logger
from .sessions import SecureCookieSessionInterface from .sessions import SecureCookieSessionInterface
from .signals import ( from .signals import (
@ -2001,6 +2002,9 @@ class Flask(_PackageBoundObject):
``bytes`` (``str`` in Python 2) ``bytes`` (``str`` in Python 2)
A response object is created with the bytes as the body. A response object is created with the bytes as the body.
``dict``
A dictionary that will be jsonify'd before being returned.
``tuple`` ``tuple``
Either ``(body, status, headers)``, ``(body, status)``, or Either ``(body, status, headers)``, ``(body, status)``, or
``(body, headers)``, where ``body`` is any of the other types ``(body, headers)``, where ``body`` is any of the other types
@ -2064,6 +2068,8 @@ class Flask(_PackageBoundObject):
# special logic # special logic
rv = self.response_class(rv, status=status, headers=headers) rv = self.response_class(rv, status=status, headers=headers)
status = headers = None status = headers = None
elif isinstance(rv, dict):
rv = jsonify(rv)
else: else:
# evaluate a WSGI callable, or coerce a different response # evaluate a WSGI callable, or coerce a different response
# class to the correct type # class to the correct type

View File

@ -1147,8 +1147,12 @@ def test_response_types(app, client):
def from_wsgi(): def from_wsgi():
return NotFound() return NotFound()
assert client.get("/text").data == u"Hällo Wörld".encode("utf-8") @app.route('/dict')
assert client.get("/bytes").data == u"Hällo Wörld".encode("utf-8") def from_dict():
return {"foo": "bar"}, 201
assert client.get('/text').data == u'Hällo Wörld'.encode('utf-8')
assert client.get('/bytes').data == u'Hällo Wörld'.encode('utf-8')
rv = client.get("/full_tuple") rv = client.get("/full_tuple")
assert rv.data == b"Meh" assert rv.data == b"Meh"
@ -1181,6 +1185,10 @@ def test_response_types(app, client):
assert b"Not Found" in rv.data assert b"Not Found" in rv.data
assert rv.status_code == 404 assert rv.status_code == 404
rv = client.get('/dict')
assert rv.json == {"foo": "bar"}
assert rv.status_code == 201
def test_response_type_errors(): def test_response_type_errors():
app = flask.Flask(__name__) app = flask.Flask(__name__)