Browse Source

Mixin for JSON decoding code shared between request/response

reviewable/pr1984/r1
Adam Byrtek 10 years ago committed by Markus Unterwaditzer
parent
commit
c9ef500c5c
  1. 218
      flask/wrappers.py

218
flask/wrappers.py

@ -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
return False
.. versionadded:: 0.12
"""
@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…
Cancel
Save