|
1 This demonstrates how the Request object works, and tests it. |
|
2 |
|
3 You can instantiate a request using ``Request.blank()``, to create a |
|
4 fresh environment dictionary with all the basic keys such a dictionary |
|
5 should have. |
|
6 |
|
7 >>> from dtopt import ELLIPSIS |
|
8 >>> from webob import Request, UTC |
|
9 >>> req = Request.blank('/') |
|
10 >>> req # doctest: +ELLIPSIS |
|
11 <Request at ... GET http://localhost/> |
|
12 >>> print repr(str(req)) |
|
13 'GET /\r\nHost: localhost:80\r\n\r\n' |
|
14 >>> req.environ # doctest: +ELLIPSIS |
|
15 {...} |
|
16 >>> req.body_file # doctest: +ELLIPSIS |
|
17 <cStringIO.StringI object at ...> |
|
18 >>> req.scheme |
|
19 'http' |
|
20 >>> req.method |
|
21 'GET' |
|
22 >>> req.script_name |
|
23 '' |
|
24 >>> req.path_info |
|
25 '/' |
|
26 >>> req.content_type |
|
27 '' |
|
28 >>> print req.remote_user |
|
29 None |
|
30 >>> req.host_url |
|
31 'http://localhost' |
|
32 >>> req.script_name = '/foo' |
|
33 >>> req.path_info = '/bar/' |
|
34 >>> req.environ['QUERY_STRING'] = 'a=b' |
|
35 >>> req.application_url |
|
36 'http://localhost/foo' |
|
37 >>> req.path_url |
|
38 'http://localhost/foo/bar/' |
|
39 >>> req.url |
|
40 'http://localhost/foo/bar/?a=b' |
|
41 >>> req.relative_url('baz') |
|
42 'http://localhost/foo/bar/baz' |
|
43 >>> req.relative_url('baz', to_application=True) |
|
44 'http://localhost/foo/baz' |
|
45 >>> req.relative_url('http://example.org') |
|
46 'http://example.org' |
|
47 >>> req.path_info_peek() |
|
48 'bar' |
|
49 >>> req.path_info_pop() |
|
50 'bar' |
|
51 >>> req.script_name, req.path_info |
|
52 ('/foo/bar', '/') |
|
53 >>> print req.environ.get('wsgiorg.routing_args') |
|
54 None |
|
55 >>> req.urlvars |
|
56 {} |
|
57 >>> req.environ['wsgiorg.routing_args'] |
|
58 ((), {}) |
|
59 >>> req.urlvars = dict(x='y') |
|
60 >>> req.environ['wsgiorg.routing_args'] |
|
61 ((), {'x': 'y'}) |
|
62 >>> req.urlargs |
|
63 () |
|
64 >>> req.urlargs = (1, 2, 3) |
|
65 >>> req.environ['wsgiorg.routing_args'] |
|
66 ((1, 2, 3), {'x': 'y'}) |
|
67 >>> del req.urlvars |
|
68 >>> req.environ['wsgiorg.routing_args'] |
|
69 ((1, 2, 3), {}) |
|
70 >>> req.urlvars = {'test': 'value'} |
|
71 >>> del req.urlargs |
|
72 >>> req.environ['wsgiorg.routing_args'] |
|
73 ((), {'test': 'value'}) |
|
74 >>> req.is_xhr |
|
75 False |
|
76 >>> req.environ['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' |
|
77 >>> req.is_xhr |
|
78 True |
|
79 >>> req.host |
|
80 'localhost:80' |
|
81 |
|
82 There are also variables to access the variables and body: |
|
83 |
|
84 >>> from cStringIO import StringIO |
|
85 >>> body = 'var1=value1&var2=value2&rep=1&rep=2' |
|
86 >>> req = Request.blank('/') |
|
87 >>> req.method = 'POST' |
|
88 >>> req.body_file = StringIO(body) |
|
89 >>> req.environ['CONTENT_LENGTH'] = str(len(body)) |
|
90 >>> vars = req.str_POST |
|
91 >>> vars |
|
92 MultiDict([('var1', 'value1'), ('var2', 'value2'), ('rep', '1'), ('rep', '2')]) |
|
93 >>> vars is req.str_POST |
|
94 True |
|
95 >>> req.POST |
|
96 MultiDict([('var1', 'value1'), ('var2', 'value2'), ('rep', '1'), ('rep', '2')]) |
|
97 >>> req.charset = 'utf8' |
|
98 >>> req.POST |
|
99 UnicodeMultiDict([(u'var1', u'value1'), (u'var2', u'value2'), (u'rep', u'1'), (u'rep', u'2')]) |
|
100 |
|
101 Note that the variables are there for GET requests and non-form POST |
|
102 requests, but they are empty and read-only: |
|
103 |
|
104 >>> req = Request.blank('/') |
|
105 >>> req.str_POST |
|
106 <NoVars: Not a POST request> |
|
107 >>> req.str_POST.items() |
|
108 [] |
|
109 >>> req.str_POST['x'] = 'y' |
|
110 Traceback (most recent call last): |
|
111 ... |
|
112 KeyError: 'Cannot add variables: Not a POST request' |
|
113 >>> req.method = 'POST' |
|
114 >>> req.str_POST |
|
115 MultiDict([]) |
|
116 >>> req.content_type = 'text/xml' |
|
117 >>> req.body_file = StringIO('<xml></xml>') |
|
118 >>> req.str_POST |
|
119 <NoVars: Not an HTML form submission (Content-Type: text/xml)> |
|
120 >>> req.body |
|
121 '<xml></xml>' |
|
122 |
|
123 You can also get access to the query string variables, of course: |
|
124 |
|
125 >>> req = Request.blank('/?a=b&d=e&d=f') |
|
126 >>> req.GET |
|
127 MultiDict([('a', 'b'), ('d', 'e'), ('d', 'f')]) |
|
128 >>> req.GET['d'] |
|
129 'f' |
|
130 >>> req.GET.getall('d') |
|
131 ['e', 'f'] |
|
132 >>> req.method = 'POST' |
|
133 >>> req.body = 'x=y&d=g' |
|
134 >>> req.body_file # doctest: +ELLIPSIS |
|
135 <cStringIO.StringI object at ...> |
|
136 >>> req.environ['CONTENT_LENGTH'] |
|
137 '7' |
|
138 >>> req.params |
|
139 NestedMultiDict([('a', 'b'), ('d', 'e'), ('d', 'f'), ('x', 'y'), ('d', 'g')]) |
|
140 >>> req.params['d'] |
|
141 'f' |
|
142 >>> req.params.getall('d') |
|
143 ['e', 'f', 'g'] |
|
144 |
|
145 Cookie are viewed as a dictionary (*view only*): |
|
146 |
|
147 >>> req = Request.blank('/') |
|
148 >>> req.environ['HTTP_COOKIE'] = 'var1=value1; var2=value2' |
|
149 >>> req.str_cookies |
|
150 {'var1': 'value1', 'var2': 'value2'} |
|
151 >>> req.cookies |
|
152 {'var1': 'value1', 'var2': 'value2'} |
|
153 >>> req.charset = 'utf8' |
|
154 >>> req.cookies |
|
155 UnicodeMultiDict([(u'var1', u'value1'), (u'var2', u'value2')]) |
|
156 |
|
157 Sometimes conditional headers are problematic. You can remove them: |
|
158 |
|
159 >>> from datetime import datetime |
|
160 >>> req = Request.blank('/') |
|
161 >>> req.if_match = 'some-etag' |
|
162 >>> req.if_modified_since = datetime(2005, 1, 1, 12, 0) |
|
163 >>> req.environ['HTTP_ACCEPT_ENCODING'] = 'gzip' |
|
164 >>> print req.headers |
|
165 {'Host': 'localhost:80', 'If-Match': 'some-etag', 'Accept-Encoding': 'gzip', 'If-Modified-Since': 'Sat, 01 Jan 2005 12:00:00 GMT'} |
|
166 >>> req.remove_conditional_headers() |
|
167 >>> print req.headers |
|
168 {'Host': 'localhost:80'} |
|
169 |
|
170 Some headers are handled specifically (more should be added): |
|
171 |
|
172 >>> req = Request.blank('/') |
|
173 >>> req.if_none_match = 'xxx' |
|
174 >>> 'xxx' in req.if_none_match |
|
175 True |
|
176 >>> 'yyy' in req.if_none_match |
|
177 False |
|
178 >>> req.if_modified_since = datetime(2005, 1, 1, 12, 0) |
|
179 >>> req.if_modified_since < datetime(2006, 1, 1, 12, 0, tzinfo=UTC) |
|
180 True |
|
181 >>> req.user_agent |
|
182 '' |
|
183 >>> req.user_agent = 'MSIE-Win' |
|
184 >>> req.user_agent |
|
185 'MSIE-Win' |
|
186 |
|
187 Accept-* headers are parsed into read-only objects that support |
|
188 containment tests, and some useful methods. Note that parameters on |
|
189 mime types are not supported. |
|
190 |
|
191 >>> req = Request.blank('/') |
|
192 >>> req.environ['HTTP_ACCEPT'] = "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5" |
|
193 >>> req.accept # doctest: +ELLIPSIS |
|
194 <MIMEAccept at ... Accept: text/*;q=0.3, text/html;q=0.7, text/html, text/html;q=0.4, */*;q=0.5> |
|
195 >>> for item, quality in req.accept._parsed: |
|
196 ... print '%s: %0.1f' % (item, quality) |
|
197 text/*: 0.3 |
|
198 text/html: 0.7 |
|
199 text/html: 1.0 |
|
200 text/html: 0.4 |
|
201 */*: 0.5 |
|
202 >>> '%0.1f' % req.accept.quality('text/html') |
|
203 '0.3' |
|
204 >>> req.accept.first_match(['text/plain', 'text/html', 'image/png']) |
|
205 'text/plain' |
|
206 >>> 'image/png' in req.accept |
|
207 True |
|
208 >>> req.environ['HTTP_ACCEPT'] = "text/html, application/xml; q=0.7, text/*; q=0.5, */*; q=0.1" |
|
209 >>> req.accept # doctest: +ELLIPSIS |
|
210 <MIMEAccept at ... Accept: text/html, application/xml;q=0.7, text/*;q=0.5, */*;q=0.1> |
|
211 >>> req.accept.best_match(['text/plain', 'application/xml']) |
|
212 'application/xml' |
|
213 >>> req.accept.first_match(['application/xml', 'text/html']) |
|
214 'application/xml' |
|
215 >>> req.accept = "text/html, application/xml, text/*; q=0.5" |
|
216 >>> 'image/png' in req.accept |
|
217 False |
|
218 >>> 'text/plain' in req.accept |
|
219 True |
|
220 >>> req.accept_charset = 'utf8' |
|
221 >>> 'UTF8' in req.accept_charset |
|
222 True |
|
223 >>> 'gzip' in req.accept_encoding |
|
224 False |
|
225 >>> req.accept_encoding = 'gzip' |
|
226 >>> 'GZIP' in req.accept_encoding |
|
227 True |
|
228 >>> req.accept_language = {'en-US': 0.5, 'es': 0.7} |
|
229 >>> str(req.accept_language) |
|
230 'es;q=0.7, en-US;q=0.5' |
|
231 >>> req.headers['Accept-Language'] |
|
232 'es;q=0.7, en-US;q=0.5' |
|
233 >>> req.accept_language.best_matches('en-GB') |
|
234 ['es', 'en-US', 'en-GB'] |
|
235 >>> req.accept_language.best_matches('es') |
|
236 ['es'] |
|
237 >>> req.accept_language.best_matches('ES') |
|
238 ['es'] |
|
239 |
|
240 The If-Range header is a combination of a possible conditional date or |
|
241 etag match:: |
|
242 |
|
243 >>> req = Request.blank('/') |
|
244 >>> req.if_range = 'asdf' |
|
245 >>> req.if_range |
|
246 <IfRange etag=asdf, date=*> |
|
247 >>> from webob import Response |
|
248 >>> res = Response() |
|
249 >>> res.etag = 'asdf' |
|
250 >>> req.if_range.match_response(res) |
|
251 True |
|
252 >>> res.etag = None |
|
253 >>> req.if_range.match_response(res) |
|
254 False |
|
255 >>> res.last_modified = datetime(2005, 1, 1, 12, 0, tzinfo=UTC) |
|
256 >>> req.if_range = datetime(2006, 1, 1, 12, 0, tzinfo=UTC) |
|
257 >>> req.if_range |
|
258 <IfRange etag=*, date=Sun, 01 Jan 2006 12:00:00 GMT> |
|
259 >>> req.if_range.match_response(res) |
|
260 True |
|
261 >>> res.last_modified = datetime(2007, 1, 1, 12, 0, tzinfo=UTC) |
|
262 >>> req.if_range.match_response(res) |
|
263 False |
|
264 >>> req = Request.blank('/') |
|
265 >>> req.if_range |
|
266 <Empty If-Range> |
|
267 >>> req.if_range.match_response(res) |
|
268 True |
|
269 |
|
270 Ranges work like so:: |
|
271 |
|
272 >>> req = Request.blank('/') |
|
273 >>> req.range = (0, 100) |
|
274 >>> req.range |
|
275 <Range ranges=(0, 100)> |
|
276 >>> str(req.range) |
|
277 'bytes=0-101' |
|
278 |
|
279 You can use them with responses:: |
|
280 |
|
281 >>> res = Response() |
|
282 >>> res.content_range = req.range.content_range(1000) |
|
283 >>> res.content_range |
|
284 <ContentRange bytes 0-101/1000> |
|
285 >>> str(res.content_range) |
|
286 'bytes 0-101/1000' |
|
287 >>> start, end, length = res.content_range |
|
288 >>> start, end, length |
|
289 (0, 100, 1000) |
|
290 |
|
291 A quick test of caching the request body: |
|
292 |
|
293 >>> from cStringIO import StringIO |
|
294 >>> length = Request.request_body_tempfile_limit+10 |
|
295 >>> data = StringIO('x'*length) |
|
296 >>> req = Request.blank('/') |
|
297 >>> req.content_length = length |
|
298 >>> req.body_file = data |
|
299 >>> req.body_file |
|
300 <cStringIO.StringI object at ...> |
|
301 >>> len(req.body) |
|
302 10250 |
|
303 >>> req.body_file |
|
304 <open file '<fdopen>', mode 'w+b' at ...> |
|
305 |
|
306 Some query tests: |
|
307 |
|
308 >>> req = Request.blank('/') |
|
309 >>> req.GET.get('unknown') |
|
310 >>> req.GET.get('unknown', '?') |
|
311 '?' |
|
312 >>> req.POST.get('unknown') |
|
313 >>> req.POST.get('unknown', '?') |
|
314 '?' |
|
315 >>> req.params.get('unknown') |
|
316 >>> req.params.get('unknown', '?') |
|
317 '?' |