wrappers.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. # -*- coding: utf-8 -*-
  2. """
  3. flask.wrappers
  4. ~~~~~~~~~~~~~~
  5. Implements the WSGI wrappers (request and response).
  6. :copyright: (c) 2015 by Armin Ronacher.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase
  10. from werkzeug.exceptions import BadRequest
  11. from . import json
  12. from .globals import _request_ctx_stack
  13. _missing = object()
  14. def _get_data(req, cache):
  15. getter = getattr(req, 'get_data', None)
  16. if getter is not None:
  17. return getter(cache=cache)
  18. return req.data
  19. class Request(RequestBase):
  20. """The request object used by default in Flask. Remembers the
  21. matched endpoint and view arguments.
  22. It is what ends up as :class:`~flask.request`. If you want to replace
  23. the request object used you can subclass this and set
  24. :attr:`~flask.Flask.request_class` to your subclass.
  25. The request object is a :class:`~werkzeug.wrappers.Request` subclass and
  26. provides all of the attributes Werkzeug defines plus a few Flask
  27. specific ones.
  28. """
  29. #: The internal URL rule that matched the request. This can be
  30. #: useful to inspect which methods are allowed for the URL from
  31. #: a before/after handler (``request.url_rule.methods``) etc.
  32. #:
  33. #: .. versionadded:: 0.6
  34. url_rule = None
  35. #: A dict of view arguments that matched the request. If an exception
  36. #: happened when matching, this will be ``None``.
  37. view_args = None
  38. #: If matching the URL failed, this is the exception that will be
  39. #: raised / was raised as part of the request handling. This is
  40. #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
  41. #: something similar.
  42. routing_exception = None
  43. # Switched by the request context until 1.0 to opt in deprecated
  44. # module functionality.
  45. _is_old_module = False
  46. @property
  47. def max_content_length(self):
  48. """Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""
  49. ctx = _request_ctx_stack.top
  50. if ctx is not None:
  51. return ctx.app.config['MAX_CONTENT_LENGTH']
  52. @property
  53. def endpoint(self):
  54. """The endpoint that matched the request. This in combination with
  55. :attr:`view_args` can be used to reconstruct the same or a
  56. modified URL. If an exception happened when matching, this will
  57. be ``None``.
  58. """
  59. if self.url_rule is not None:
  60. return self.url_rule.endpoint
  61. @property
  62. def module(self):
  63. """The name of the current module if the request was dispatched
  64. to an actual module. This is deprecated functionality, use blueprints
  65. instead.
  66. """
  67. from warnings import warn
  68. warn(DeprecationWarning('modules were deprecated in favor of '
  69. 'blueprints. Use request.blueprint '
  70. 'instead.'), stacklevel=2)
  71. if self._is_old_module:
  72. return self.blueprint
  73. @property
  74. def blueprint(self):
  75. """The name of the current blueprint"""
  76. if self.url_rule and '.' in self.url_rule.endpoint:
  77. return self.url_rule.endpoint.rsplit('.', 1)[0]
  78. @property
  79. def json(self):
  80. """If the mimetype is :mimetype:`application/json` this will contain the
  81. parsed JSON data. Otherwise this will be ``None``.
  82. The :meth:`get_json` method should be used instead.
  83. """
  84. from warnings import warn
  85. warn(DeprecationWarning('json is deprecated. '
  86. 'Use get_json() instead.'), stacklevel=2)
  87. return self.get_json()
  88. @property
  89. def is_json(self):
  90. """Indicates if this request is JSON or not. By default a request
  91. is considered to include JSON data if the mimetype is
  92. :mimetype:`application/json` or :mimetype:`application/*+json`.
  93. .. versionadded:: 0.11
  94. """
  95. mt = self.mimetype
  96. if mt == 'application/json':
  97. return True
  98. if mt.startswith('application/') and mt.endswith('+json'):
  99. return True
  100. return False
  101. def get_json(self, force=False, silent=False, cache=True):
  102. """Parses the incoming JSON request data and returns it. By default
  103. this function will return ``None`` if the mimetype is not
  104. :mimetype:`application/json` but this can be overridden by the
  105. ``force`` parameter. If parsing fails the
  106. :meth:`on_json_loading_failed` method on the request object will be
  107. invoked.
  108. :param force: if set to ``True`` the mimetype is ignored.
  109. :param silent: if set to ``True`` this method will fail silently
  110. and return ``None``.
  111. :param cache: if set to ``True`` the parsed JSON data is remembered
  112. on the request.
  113. """
  114. rv = getattr(self, '_cached_json', _missing)
  115. if rv is not _missing:
  116. return rv
  117. if not (force or self.is_json):
  118. return None
  119. # We accept a request charset against the specification as
  120. # certain clients have been using this in the past. This
  121. # fits our general approach of being nice in what we accept
  122. # and strict in what we send out.
  123. request_charset = self.mimetype_params.get('charset')
  124. try:
  125. data = _get_data(self, cache)
  126. if request_charset is not None:
  127. rv = json.loads(data, encoding=request_charset)
  128. else:
  129. rv = json.loads(data)
  130. except ValueError as e:
  131. if silent:
  132. rv = None
  133. else:
  134. rv = self.on_json_loading_failed(e)
  135. if cache:
  136. self._cached_json = rv
  137. return rv
  138. def on_json_loading_failed(self, e):
  139. """Called if decoding of the JSON data failed. The return value of
  140. this method is used by :meth:`get_json` when an error occurred. The
  141. default implementation just raises a :class:`BadRequest` exception.
  142. .. versionchanged:: 0.10
  143. Removed buggy previous behavior of generating a random JSON
  144. response. If you want that behavior back you can trivially
  145. add it by subclassing.
  146. .. versionadded:: 0.8
  147. """
  148. ctx = _request_ctx_stack.top
  149. if ctx is not None and ctx.app.config.get('DEBUG', False):
  150. raise BadRequest('Failed to decode JSON object: {0}'.format(e))
  151. raise BadRequest()
  152. def _load_form_data(self):
  153. RequestBase._load_form_data(self)
  154. # In debug mode we're replacing the files multidict with an ad-hoc
  155. # subclass that raises a different error for key errors.
  156. ctx = _request_ctx_stack.top
  157. if ctx is not None and ctx.app.debug and \
  158. self.mimetype != 'multipart/form-data' and not self.files:
  159. from .debughelpers import attach_enctype_error_multidict
  160. attach_enctype_error_multidict(self)
  161. class Response(ResponseBase):
  162. """The response object that is used by default in Flask. Works like the
  163. response object from Werkzeug but is set to have an HTML mimetype by
  164. default. Quite often you don't have to create this object yourself because
  165. :meth:`~flask.Flask.make_response` will take care of that for you.
  166. If you want to replace the response object used you can subclass this and
  167. set :attr:`~flask.Flask.response_class` to your subclass.
  168. """
  169. default_mimetype = 'text/html'