use Jinja's tojson filter

This commit is contained in:
David Lord 2021-02-01 22:41:49 -08:00
parent fdf5d11b51
commit b473e7c97c
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
7 changed files with 28 additions and 88 deletions

View file

@ -63,6 +63,7 @@ Unreleased
- The request context does route matching before opening the session.
This could allow a session interface to change behavior based on
``request.endpoint``. :issue:`3776`
- Use Jinja's implementation of the ``|tojson`` filter. :issue:`3881`
Version 1.1.2

View file

@ -250,14 +250,15 @@ for easier customization. By default it handles some extra data types:
- :class:`~markupsafe.Markup` (or any object with a ``__html__``
method) will call the ``__html__`` method to get a string.
:func:`~htmlsafe_dumps` is also available as the ``|tojson`` template
filter. The filter marks the output with ``|safe`` so it can be used
inside ``script`` tags.
Jinja's ``|tojson`` filter is configured to use Flask's :func:`dumps`
function. The filter marks the output with ``|safe`` automatically. Use
the filter to render data inside ``<script>`` tags.
.. sourcecode:: html+jinja
<script type=text/javascript>
renderChart({{ axis_data|tojson }});
const names = {{ names|tosjon }};
renderChart(names, {{ axis_data|tojson }});
</script>
.. autofunction:: jsonify

View file

@ -60,27 +60,9 @@ like this:
.. sourcecode:: html+jinja
<script type=text/javascript>
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
$SCRIPT_ROOT = {{ request.script_root|tojson }};
</script>
The ``|safe`` is necessary in Flask before 0.10 so that Jinja does not
escape the JSON encoded string with HTML rules. Usually this would be
necessary, but we are inside a ``script`` block here where different rules
apply.
.. admonition:: Information for Pros
In HTML the ``script`` tag is declared ``CDATA`` which means that entities
will not be parsed. Everything until ``</script>`` is handled as script.
This also means that there must never be any ``</`` between the script
tags. ``|tojson`` is kind enough to do the right thing here and
escape slashes for you (``{{ "</script>"|tojson|safe }}`` is rendered as
``"<\/script>"``).
In Flask 0.10 it goes a step further and escapes all HTML tags with
unicode escapes. This makes it possible for Flask to automatically
mark the result as HTML safe.
JSON View Functions
-------------------

View file

@ -95,37 +95,6 @@ by default:
{% from '_helpers.html' import my_macro with context %}
Standard Filters
----------------
Flask provides the following Jinja2 filters in addition to the filters provided
by Jinja2 itself:
.. function:: tojson
:noindex:
This function converts the given object into JSON representation. This
is for example very helpful if you try to generate JavaScript on the
fly.
.. sourcecode:: html+jinja
<script type=text/javascript>
doSomethingWith({{ user.username|tojson }});
</script>
It is also safe to use the output of `|tojson` in a *single-quoted* HTML
attribute:
.. sourcecode:: html+jinja
<button onclick='doSomethingWith({{ user.username|tojson }})'>
Click me
</button>
Note that in versions of Flask prior to 0.10, if using the output of
``|tojson`` inside ``script``, make sure to disable escaping with ``|safe``.
In Flask 0.10 and above, this happens automatically.
Controlling Autoescaping
------------------------

View file

@ -681,7 +681,7 @@ class Flask(Scaffold):
session=session,
g=g,
)
rv.filters["tojson"] = json.tojson_filter
rv.policies["json.dumps_function"] = json.dumps
return rv
def create_global_jinja_loader(self):

View file

@ -5,7 +5,7 @@ import warnings
from datetime import date
from datetime import datetime
from markupsafe import Markup
from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps
from werkzeug.http import http_date
from ..globals import current_app
@ -234,30 +234,28 @@ def load(fp, app=None, **kwargs):
return _json.load(fp, **kwargs)
_htmlsafe_map = str.maketrans(
{"<": "\\u003c", ">": "\\u003e", "&": "\\u0026", "'": "\\u0027"}
)
def htmlsafe_dumps(obj, **kwargs):
"""Serialize an object to a string of JSON, replacing HTML-unsafe
characters with Unicode escapes. Otherwise behaves the same as
:func:`dumps`.
"""Serialize an object to a string of JSON with :func:`dumps`, then
replace HTML-unsafe characters with Unicode escapes and mark the
result safe with :class:`~markupsafe.Markup`.
This is available in templates as the ``|tojson`` filter, which will
also mark the result with ``|safe``.
This is available in templates as the ``|tojson`` filter.
The returned string is safe to render in HTML documents and
``<script>`` tags. The exception is in HTML attributes that are
double quoted; either use single quotes or the ``|forceescape``
filter.
.. versionchanged:: 2.0
Uses :func:`jinja2.utils.htmlsafe_json_dumps`. The returned
value is marked safe by wrapping in :class:`~markupsafe.Markup`.
.. versionchanged:: 0.10
Single quotes are escaped, making this safe to use in HTML,
``<script>`` tags, and single-quoted attributes without further
escaping.
"""
return dumps(obj, **kwargs).translate(_htmlsafe_map)
return _jinja_htmlsafe_dumps(obj, dumps=dumps, **kwargs)
def htmlsafe_dump(obj, fp, **kwargs):
@ -335,7 +333,3 @@ def jsonify(*args, **kwargs):
f"{dumps(data, indent=indent, separators=separators)}\n",
mimetype=current_app.config["JSONIFY_MIMETYPE"],
)
def tojson_filter(obj, **kwargs):
return Markup(htmlsafe_dumps(obj, **kwargs))

View file

@ -204,24 +204,17 @@ def test_json_attr(app, client):
assert rv.data == b"3"
def test_template_escaping(app, req_ctx):
render = flask.render_template_string
rv = flask.json.htmlsafe_dumps("</script>")
assert rv == '"\\u003c/script\\u003e"'
rv = render('{{ "</script>"|tojson }}')
assert rv == '"\\u003c/script\\u003e"'
rv = render('{{ "<\0/script>"|tojson }}')
assert rv == '"\\u003c\\u0000/script\\u003e"'
rv = render('{{ "<!--<script>"|tojson }}')
assert rv == '"\\u003c!--\\u003cscript\\u003e"'
rv = render('{{ "&"|tojson }}')
assert rv == '"\\u0026"'
rv = render('{{ "\'"|tojson }}')
assert rv == '"\\u0027"'
rv = render(
"<a ng-data='{{ data|tojson }}'></a>", data={"x": ["foo", "bar", "baz'"]}
def test_tojson_filter(app, req_ctx):
# The tojson filter is tested in Jinja, this confirms that it's
# using Flask's dumps.
rv = flask.render_template_string(
"const data = {{ data|tojson }};",
data={"name": "</script>", "time": datetime.datetime(2021, 2, 1, 7, 15)},
)
assert rv == (
'const data = {"name": "\\u003c/script\\u003e",'
' "time": "Mon, 01 Feb 2021 07:15:00 GMT"};'
)
assert rv == '<a ng-data=\'{"x": ["foo", "bar", "baz\\u0027"]}\'></a>'
def test_json_customization(app, client):