Mixin for JSON decoding code shared between request/response
This commit is contained in:
parent
ca547f0ec3
commit
c9ef500c5c
1 changed files with 81 additions and 137 deletions
|
|
@ -25,15 +25,88 @@ def _get_data(req, cache):
|
||||||
return req.data
|
return req.data
|
||||||
|
|
||||||
|
|
||||||
def _is_mimetype_json(mimetype):
|
class JSONMixin(object):
|
||||||
if mimetype == 'application/json':
|
"""Mixin for both request and response classes to provide JSON parsing
|
||||||
return True
|
capabilities.
|
||||||
if mimetype.startswith('application/') and mimetype.endswith('+json'):
|
|
||||||
return True
|
.. versionadded:: 0.12
|
||||||
return False
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_json(self):
|
||||||
|
"""Indicates if this request/response is JSON or not. By default it
|
||||||
|
is considered to include JSON data if the mimetype is
|
||||||
|
:mimetype:`application/json` or :mimetype:`application/*+json`.
|
||||||
|
"""
|
||||||
|
mt = self.mimetype
|
||||||
|
if mt == 'application/json':
|
||||||
|
return True
|
||||||
|
if mt.startswith('application/') and mt.endswith('+json'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def json(self):
|
||||||
|
"""If the mimetype is :mimetype:`application/json` this will contain the
|
||||||
|
parsed JSON data. Otherwise this will be ``None``.
|
||||||
|
|
||||||
|
The :meth:`get_json` method should be used instead.
|
||||||
|
"""
|
||||||
|
from warnings import warn
|
||||||
|
warn(DeprecationWarning('json is deprecated. '
|
||||||
|
'Use get_json() instead.'), stacklevel=2)
|
||||||
|
return self.get_json()
|
||||||
|
|
||||||
|
def get_json(self, force=False, silent=False, cache=True):
|
||||||
|
"""Parses the incoming JSON request data and returns it. By default
|
||||||
|
this function will return ``None`` if the mimetype is not
|
||||||
|
:mimetype:`application/json` but this can be overridden by the
|
||||||
|
``force`` parameter. If parsing fails the
|
||||||
|
:meth:`on_json_loading_failed` method on the request object will be
|
||||||
|
invoked.
|
||||||
|
|
||||||
|
:param force: if set to ``True`` the mimetype is ignored.
|
||||||
|
:param silent: if set to ``True`` this method will fail silently
|
||||||
|
and return ``None``.
|
||||||
|
:param cache: if set to ``True`` the parsed JSON data is remembered
|
||||||
|
on the request.
|
||||||
|
"""
|
||||||
|
rv = getattr(self, '_cached_json', _missing)
|
||||||
|
if rv is not _missing:
|
||||||
|
return rv
|
||||||
|
|
||||||
|
if not (force or self.is_json):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# We accept a request charset against the specification as certain
|
||||||
|
# clients have been using this in the past. For responses, we assume
|
||||||
|
# that if the response charset was set explicitly then the data had
|
||||||
|
# been encoded correctly as well.
|
||||||
|
charset = self.mimetype_params.get('charset')
|
||||||
|
try:
|
||||||
|
data = _get_data(self, cache)
|
||||||
|
if charset is not None:
|
||||||
|
rv = json.loads(data, encoding=charset)
|
||||||
|
else:
|
||||||
|
rv = json.loads(data)
|
||||||
|
except ValueError as e:
|
||||||
|
if silent:
|
||||||
|
rv = None
|
||||||
|
else:
|
||||||
|
rv = self.on_json_loading_failed(e)
|
||||||
|
if cache:
|
||||||
|
self._cached_json = rv
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def on_json_loading_failed(self, e):
|
||||||
|
"""Called if decoding of the JSON data failed. The return value of
|
||||||
|
this method is used by :meth:`get_json` when an error occurred. The
|
||||||
|
default implementation just raises a :class:`BadRequest` exception.
|
||||||
|
"""
|
||||||
|
raise BadRequest()
|
||||||
|
|
||||||
|
|
||||||
class Request(RequestBase):
|
class Request(RequestBase, JSONMixin):
|
||||||
"""The request object used by default in Flask. Remembers the
|
"""The request object used by default in Flask. Remembers the
|
||||||
matched endpoint and view arguments.
|
matched endpoint and view arguments.
|
||||||
|
|
||||||
|
|
@ -103,69 +176,6 @@ class Request(RequestBase):
|
||||||
if self.url_rule and '.' in self.url_rule.endpoint:
|
if self.url_rule and '.' in self.url_rule.endpoint:
|
||||||
return self.url_rule.endpoint.rsplit('.', 1)[0]
|
return self.url_rule.endpoint.rsplit('.', 1)[0]
|
||||||
|
|
||||||
@property
|
|
||||||
def json(self):
|
|
||||||
"""If the mimetype is :mimetype:`application/json` this will contain the
|
|
||||||
parsed JSON data. Otherwise this will be ``None``.
|
|
||||||
|
|
||||||
The :meth:`get_json` method should be used instead.
|
|
||||||
"""
|
|
||||||
from warnings import warn
|
|
||||||
warn(DeprecationWarning('json is deprecated. '
|
|
||||||
'Use get_json() instead.'), stacklevel=2)
|
|
||||||
return self.get_json()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_json(self):
|
|
||||||
"""Indicates if this request is JSON or not. By default a request
|
|
||||||
is considered to include JSON data if the mimetype is
|
|
||||||
:mimetype:`application/json` or :mimetype:`application/*+json`.
|
|
||||||
|
|
||||||
.. versionadded:: 0.11
|
|
||||||
"""
|
|
||||||
return _is_mimetype_json(self.mimetype)
|
|
||||||
|
|
||||||
def get_json(self, force=False, silent=False, cache=True):
|
|
||||||
"""Parses the incoming JSON request data and returns it. By default
|
|
||||||
this function will return ``None`` if the mimetype is not
|
|
||||||
:mimetype:`application/json` but this can be overridden by the
|
|
||||||
``force`` parameter. If parsing fails the
|
|
||||||
:meth:`on_json_loading_failed` method on the request object will be
|
|
||||||
invoked.
|
|
||||||
|
|
||||||
:param force: if set to ``True`` the mimetype is ignored.
|
|
||||||
:param silent: if set to ``True`` this method will fail silently
|
|
||||||
and return ``None``.
|
|
||||||
:param cache: if set to ``True`` the parsed JSON data is remembered
|
|
||||||
on the request.
|
|
||||||
"""
|
|
||||||
rv = getattr(self, '_cached_json', _missing)
|
|
||||||
if rv is not _missing:
|
|
||||||
return rv
|
|
||||||
|
|
||||||
if not (force or self.is_json):
|
|
||||||
return None
|
|
||||||
|
|
||||||
# We accept a request charset against the specification as
|
|
||||||
# certain clients have been using this in the past. This
|
|
||||||
# fits our general approach of being nice in what we accept
|
|
||||||
# and strict in what we send out.
|
|
||||||
request_charset = self.mimetype_params.get('charset')
|
|
||||||
try:
|
|
||||||
data = _get_data(self, cache)
|
|
||||||
if request_charset is not None:
|
|
||||||
rv = json.loads(data, encoding=request_charset)
|
|
||||||
else:
|
|
||||||
rv = json.loads(data)
|
|
||||||
except ValueError as e:
|
|
||||||
if silent:
|
|
||||||
rv = None
|
|
||||||
else:
|
|
||||||
rv = self.on_json_loading_failed(e)
|
|
||||||
if cache:
|
|
||||||
self._cached_json = rv
|
|
||||||
return rv
|
|
||||||
|
|
||||||
def on_json_loading_failed(self, e):
|
def on_json_loading_failed(self, e):
|
||||||
"""Called if decoding of the JSON data failed. The return value of
|
"""Called if decoding of the JSON data failed. The return value of
|
||||||
this method is used by :meth:`get_json` when an error occurred. The
|
this method is used by :meth:`get_json` when an error occurred. The
|
||||||
|
|
@ -195,7 +205,7 @@ class Request(RequestBase):
|
||||||
attach_enctype_error_multidict(self)
|
attach_enctype_error_multidict(self)
|
||||||
|
|
||||||
|
|
||||||
class Response(ResponseBase):
|
class Response(ResponseBase, JSONMixin):
|
||||||
"""The response object that is used by default in Flask. Works like the
|
"""The response object that is used by default in Flask. Works like the
|
||||||
response object from Werkzeug but is set to have an HTML mimetype by
|
response object from Werkzeug but is set to have an HTML mimetype by
|
||||||
default. Quite often you don't have to create this object yourself because
|
default. Quite often you don't have to create this object yourself because
|
||||||
|
|
@ -205,69 +215,3 @@ class Response(ResponseBase):
|
||||||
set :attr:`~flask.Flask.response_class` to your subclass.
|
set :attr:`~flask.Flask.response_class` to your subclass.
|
||||||
"""
|
"""
|
||||||
default_mimetype = 'text/html'
|
default_mimetype = 'text/html'
|
||||||
|
|
||||||
@property
|
|
||||||
def json(self):
|
|
||||||
"""If the mimetype is :mimetype:`application/json` this will contain the
|
|
||||||
parsed JSON data. Otherwise this will be ``None``.
|
|
||||||
|
|
||||||
The :meth:`get_json` method should be used instead.
|
|
||||||
|
|
||||||
.. versionadded:: 1.0
|
|
||||||
"""
|
|
||||||
from warnings import warn
|
|
||||||
warn(DeprecationWarning('json is deprecated. '
|
|
||||||
'Use get_json() instead.'), stacklevel=2)
|
|
||||||
return self.get_json()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_json(self):
|
|
||||||
"""Indicates if this response is JSON or not. By default a response
|
|
||||||
is considered to include JSON data if the mimetype is
|
|
||||||
:mimetype:`application/json` or :mimetype:`application/*+json`.
|
|
||||||
|
|
||||||
.. versionadded:: 1.0
|
|
||||||
"""
|
|
||||||
return _is_mimetype_json(self.mimetype)
|
|
||||||
|
|
||||||
def get_json(self, force=False, silent=False, cache=True):
|
|
||||||
"""Parses the incoming JSON request data and returns it. If
|
|
||||||
parsing fails the :meth:`on_json_loading_failed` method on the
|
|
||||||
request object will be invoked. By default this function will
|
|
||||||
only load the json data if the mimetype is :mimetype:`application/json`
|
|
||||||
but this can be overridden by the `force` parameter.
|
|
||||||
|
|
||||||
:param force: if set to ``True`` the mimetype is ignored.
|
|
||||||
:param silent: if set to ``True`` this method will fail silently
|
|
||||||
and return ``None``.
|
|
||||||
:param cache: if set to ``True`` the parsed JSON data is remembered
|
|
||||||
on the request.
|
|
||||||
|
|
||||||
.. versionadded:: 1.0
|
|
||||||
"""
|
|
||||||
rv = getattr(self, '_cached_json', _missing)
|
|
||||||
if rv is not _missing:
|
|
||||||
return rv
|
|
||||||
|
|
||||||
if not (force or self.is_json):
|
|
||||||
return None
|
|
||||||
|
|
||||||
# We accept a request charset against the specification as
|
|
||||||
# certain clients have been using this in the past. This
|
|
||||||
# fits our general approach of being nice in what we accept
|
|
||||||
# and strict in what we send out.
|
|
||||||
request_charset = self.mimetype_params.get('charset')
|
|
||||||
try:
|
|
||||||
data = _get_data(self, cache)
|
|
||||||
if request_charset is not None:
|
|
||||||
rv = json.loads(data, encoding=request_charset)
|
|
||||||
else:
|
|
||||||
rv = json.loads(data)
|
|
||||||
except ValueError as e:
|
|
||||||
if silent:
|
|
||||||
rv = None
|
|
||||||
else:
|
|
||||||
rv = self.on_json_loading_failed(e)
|
|
||||||
if cache:
|
|
||||||
self._cached_json = rv
|
|
||||||
return rv
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue