wrappers.py 6.6 KB

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