1
# -*- coding: utf-8 -*-
3
werkzeug.contrib.wrappers
4
~~~~~~~~~~~~~~~~~~~~~~~~~
6
Extra wrappers or mixins contributed by the community. These wrappers can
7
be mixed in into request objects to add extra functionality.
11
from werkzeug import Request as RequestBase
12
from werkzeug.contrib.wrappers import JSONRequestMixin
14
class Request(RequestBase, JSONRequestMixin):
17
Afterwards this request object provides the extra functionality of the
18
:class:`JSONRequestMixin`.
20
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
21
:license: BSD, see LICENSE for more details.
23
from werkzeug.exceptions import BadRequest
24
from werkzeug.utils import cached_property
25
from werkzeug._internal import _decode_unicode
27
from simplejson import loads
29
from json import loads
32
class JSONRequestMixin(object):
33
"""Add json method to a request object. This will parse the input data
34
through simplejson if possible.
36
:exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type
37
is not json or if the data itself cannot be parsed as json.
42
"""Get the result of simplejson.loads if possible."""
43
if 'json' not in self.environ.get('CONTENT_TYPE', ''):
44
raise BadRequest('Not a JSON request')
46
return loads(self.data)
48
raise BadRequest('Unable to read JSON request')
51
class ProtobufRequestMixin(object):
52
"""Add protobuf parsing method to a request object. This will parse the
53
input data through `protobuf`_ if possible.
55
:exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type
56
is not protobuf or if the data itself cannot be parsed property.
58
.. _protobuf: http://code.google.com/p/protobuf/
61
#: by default the :class:`ProtobufRequestMixin` will raise a
62
#: :exc:`~werkzeug.exceptions.BadRequest` if the object is not
63
#: initialized. You can bypass that check by setting this
64
#: attribute to `False`.
65
protobuf_check_initialization = True
67
def parse_protobuf(self, proto_type):
68
"""Parse the data into an instance of proto_type."""
69
if 'protobuf' not in self.environ.get('CONTENT_TYPE', ''):
70
raise BadRequest('Not a Protobuf request')
74
obj.ParseFromString(self.data)
76
raise BadRequest("Unable to parse Protobuf request")
78
# Fail if not all required fields are set
79
if self.protobuf_check_initialization and not obj.IsInitialized():
80
raise BadRequest("Partial Protobuf request")
85
class RoutingArgsRequestMixin(object):
86
"""This request mixin adds support for the wsgiorg routing args
89
.. _specification: http://www.wsgi.org/wsgi/Specifications/routing_args
92
def _get_routing_args(self):
93
return self.environ.get('wsgiorg.routing_args', (()))[0]
95
def _set_routing_args(self, value):
97
raise RuntimeError('A shallow request tried to modify the WSGI '
98
'environment. If you really want to do that, '
99
'set `shallow` to False.')
100
self.environ['wsgiorg.routing_args'] = (value, self.routing_vars)
102
routing_args = property(_get_routing_args, _set_routing_args, doc='''
103
The positional URL arguments as `tuple`.''')
104
del _get_routing_args, _set_routing_args
106
def _get_routing_vars(self):
107
rv = self.environ.get('wsgiorg.routing_args')
112
self.routing_vars = rv
115
def _set_routing_vars(self, value):
117
raise RuntimeError('A shallow request tried to modify the WSGI '
118
'environment. If you really want to do that, '
119
'set `shallow` to False.')
120
self.environ['wsgiorg.routing_args'] = (self.routing_args, value)
122
routing_vars = property(_get_routing_vars, _set_routing_vars, doc='''
123
The keyword URL arguments as `dict`.''')
124
del _get_routing_vars, _set_routing_vars
127
class ReverseSlashBehaviorRequestMixin(object):
128
"""This mixin reverses the trailing slash behavior of :attr:`script_root`
129
and :attr:`path`. This makes it possible to use :func:`~urlparse.urljoin`
130
directly on the paths.
132
Because it changes the behavior or :class:`Request` this class has to be
133
mixed in *before* the actual request class::
135
class MyRequest(ReverseSlashBehaviorRequestMixin, Request):
138
This example shows the differences (for an application mounted on
139
`/application` and the request going to `/application/foo/bar`):
141
+---------------+-------------------+---------------------+
142
| | normal behavior | reverse behavior |
143
+===============+===================+=====================+
144
| `script_root` | ``/application`` | ``/application/`` |
145
+---------------+-------------------+---------------------+
146
| `path` | ``/foo/bar`` | ``foo/bar`` |
147
+---------------+-------------------+---------------------+
152
"""Requested path as unicode. This works a bit like the regular path
153
info in the WSGI environment but will not include a leading slash.
155
path = (self.environ.get('PATH_INFO') or '').lstrip('/')
156
return _decode_unicode(path, self.charset, self.encoding_errors)
159
def script_root(self):
160
"""The root path of the script includling a trailing slash."""
161
path = (self.environ.get('SCRIPT_NAME') or '').rstrip('/') + '/'
162
return _decode_unicode(path, self.charset, self.encoding_errors)