Skip to content

Commit

Permalink
Allow dictionary return values as JSON
Browse files Browse the repository at this point in the history
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",
         }
  • Loading branch information
pgjones authored and davidism committed May 24, 2019
1 parent 2616d97 commit 7bf8366
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Expand Up @@ -52,6 +52,10 @@ Unreleased
- Add an ``--extra-files`` option to the ``flask run`` CLI command to
specify extra files that will trigger the reloader on change.
: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
.. _#2957: https://github.com/pallets/flask/issues/2957
Expand Down
31 changes: 17 additions & 14 deletions docs/quickstart.rst
Expand Up @@ -679,23 +679,26 @@ See :ref:`error-handlers` for more details.
About Responses
---------------

The return value from a view function is automatically converted into a
response object for you. If the return value is a string it's converted
into a response object with the string as response body, a ``200 OK``
status code and a :mimetype:`text/html` mimetype.
The logic that Flask applies to converting return values into
response objects is as follows:
The return value from a view function is automatically converted into
a response object for you. If the return value is a string it's
converted into a response object with the string as response body, a
``200 OK`` status code and a :mimetype:`text/html` mimetype. If the
return value is a dict, ``jsonify`` is called to produce a response.
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)``,
``(response, headers)`` or ``(response, status)`` 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
2. If it's a string, a response object is created with that data and
the default parameters.
3. If it's a dict, a response object is created using ``jsonify``.
4. If a tuple is returned the items in the tuple can provide extra
information. Such tuples have to be in the form
``(response, status)``, ``(response, headers)``, or
``(response, status, headers)``. The ``status`` value will override
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.

If you want to get hold of the resulting response object inside the view
Expand Down
6 changes: 6 additions & 0 deletions flask/app.py
Expand Up @@ -44,6 +44,7 @@
url_for,
get_load_dotenv,
)
from .json import jsonify
from .logging import create_logger
from .sessions import SecureCookieSessionInterface
from .signals import (
Expand Down Expand Up @@ -2001,6 +2002,9 @@ def make_response(self, rv):
``bytes`` (``str`` in Python 2)
A response object is created with the bytes as the body.
``dict``
A dictionary that will be jsonify'd before being returned.
``tuple``
Either ``(body, status, headers)``, ``(body, status)``, or
``(body, headers)``, where ``body`` is any of the other types
Expand Down Expand Up @@ -2064,6 +2068,8 @@ def make_response(self, rv):
# special logic
rv = self.response_class(rv, status=status, headers=headers)
status = headers = None
elif isinstance(rv, dict):
rv = jsonify(rv)
else:
# evaluate a WSGI callable, or coerce a different response
# class to the correct type
Expand Down
12 changes: 10 additions & 2 deletions tests/test_basic.py
Expand Up @@ -1147,8 +1147,12 @@ def from_response_status():
def from_wsgi():
return NotFound()

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")
@app.route('/dict')
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")
assert rv.data == b"Meh"
Expand Down Expand Up @@ -1181,6 +1185,10 @@ def from_wsgi():
assert b"Not Found" in rv.data
assert rv.status_code == 404

rv = client.get('/dict')
assert rv.json == {"foo": "bar"}
assert rv.status_code == 201


def test_response_type_errors():
app = flask.Flask(__name__)
Expand Down

0 comments on commit 7bf8366

Please sign in to comment.