Merge pull request #3111 from pgjones/master

Allow dictionaries return values as JSON
This commit is contained in:
David Lord 2019-05-24 10:39:11 -07:00 committed by GitHub
commit 855d59b68b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 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, :func:`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
@ -717,6 +720,39 @@ return it::
resp.headers['X-Something'] = 'A value' resp.headers['X-Something'] = 'A value'
return resp return resp
APIs with JSON
``````````````
A common response format when writing an API is JSON. It's easy to get
started writing such an API with Flask. If you return a ``dict`` from a
view, it will be converted to a JSON response.
.. code-block:: python
@app.route("/me")
def me_api():
user = get_current_user()
return {
"username": user.username,
"theme": user.theme,
"image": url_for("user_image", filename=user.image),
}
Depending on your API design, you may want to create JSON responses for
types other than ``dict``. In that case, use the
:func:`~flask.json.jsonify` function, which will serialize any supported
JSON data type. Or look into Flask community extensions that support
more complex applications.
.. code-block:: python
@app.route("/users")
def users_api():
users = get_all_users()
return jsonify([user.to_json() for user in users])
.. _sessions: .. _sessions:
Sessions Sessions

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__)