1 Differences Between WebOb and Other Systems
2 +++++++++++++++++++++++++++++++++++++++++++
4 This document points out some of the API differences between the
5 Request and Response object, and the objects in other systems.
9 paste.wsgiwrappers and Pylons
10 =============================
12 The Pylons ``request`` and ``response`` object are based on
13 ``paste.wsgiwrappers.WSGIRequest`` and ``WSGIResponse``
15 There is no concept of ``defaults`` in WebOb. In Paste/Pylons these
16 serve as threadlocal settings that control certain policies on the
17 request and response object. In WebOb you should make your own
18 subclasses to control policy (though in many ways simply being
19 explicit elsewhere removes the need for this policy).
25 This is a file-like object in WSGIRequest. In WebOb it is a
26 string (to match Response.body) and the file-like object is
27 available through ``req.body_file``
30 This is available through ``req.accept_language``, particularly
31 ``req.accept_language.best_matches(fallback_language)``
33 ``match_accept(mimetypes)``:
34 This is available through ``req.accept.first_match(mimetypes)``;
35 or if you trust the client's quality ratings, you can use
36 ``req.accept.best_match(mimetypes)``
39 This controls how unicode decode errors are handled; it is now
40 named ``unicode_errors``
42 There are also many extra methods and attributes on WebOb Request
48 default ``content_type``:
49 The base Response object has no default content_type or charset.
50 You can set ``default_content_type`` in a subclass.
52 ``determine_charset()``:
53 Is now available as ``res.charset``
55 ``has_header(header)``:
56 Should be done with ``header in res.headers``
58 ``get_content()`` and ``wsgi_response()``:
59 These are gone; you should use ``res.body`` or ``res(environ,
63 Available in ``res.body_file.write(content)``.
65 ``flush()`` and ``tell()``:
68 There are also many extra methods and attributes on WebOb Response
74 This is a quick summary from reading `the Django documentation
75 <http://www.djangoproject.com/documentation/request_response/>`_.
87 File uploads are ``cgi.FieldStorage`` objects directly in
94 No equivalent (too connected to application model for WebOb).
95 There is ``req.remote_user``, which is only ever a string.
101 Available with ``req.body``
103 ``__getitem__(key)``:
104 You have to use ``req.params``
107 No equivalent; you could use ``req.scheme == 'https'``.
112 QueryDict is the way Django represents the multi-key dictionary-like
113 objects that are request variables (query string and POST body
114 variables). The equivalent in WebOb is MultiDict.
117 WebOb dictionaries are sometimes mutable (req.GET is,
121 I believe Django does not order the keys fully; MultiDict is a
122 full ordering. Methods that iterate over the parameters iterate
123 over keys in their order in the original request.
125 ``keys()``, ``items()``, ``values()`` (plus ``iter*``):
126 These return all values in MultiDict, but only the last value for
127 a QueryDict. That is, given ``a=1&a=2`` with MultiDict
128 ``d.items()`` returns ``[('a', '1'), ('a', '2')]``, but QueryDict
129 returns ``[('a', '1')]``
132 Available as ``d.getall(key)``
137 ``appendlist(key, value)``:
138 Available as ``d.add(key, value)``
140 ``setlistdefault(key, default_list)``:
144 Is ``d.dict_of_lists()``
146 The MultiDict object has a ``d.getone(key)`` method, that raises
147 KeyError if there is not exactly one key. There is a method
148 ``d.mixed()`` which returns a version where values are lists *if*
149 there are multiple values for a list. This is similar to how many
150 cgi-based request forms are represented.
156 Totally different. The WebOb Response object should probably be
157 subclassed for direct application use; in WebOb it does not
158 *prefer* HTML or anything normal web application conventions
161 The Django response object is somewhat dictionary-like, setting
162 headers. The equivalent dictionary-like object is
163 ``res.headers``. In WebOb this is a MultiDict.
165 ``has_header(header)``:
166 Use ``header in res.headers``
169 As ``res.body_file.write(content)``
171 ``flush()``, ``tell()``:
175 Use ``res.body`` for the ``str`` value, ``res.unicode_body`` for
176 the ``unicode`` value
181 These are generally like ``webob.exc`` objects.
182 ``HttpResponseNotModified`` is ``HTTPNotModified``; this naming
183 translation generally works.
188 The `CherryPy request object
189 <http://www.cherrypy.org/wiki/RequestObject>`_ is also used by
199 ``req.application_url``
211 A ``SimpleCookie`` object in CherryPy; a dictionary in WebOb
212 (``SimpleCookie`` can represent cookie parameters, but cookie
213 parameters are only sent with responses not requests)
216 No equivalent (this is the object dispatcher in CherryPy).
218 ``error_page``, ``error_response``, ``handle_error``:
222 Similar to ``req.get_response(app)``
227 ``headers``, ``header_list``:
228 The WSGI environment represents headers as a dictionary, available
229 through ``req.headers`` (no list form is available in the request).
237 ``methods_with_bodies``:
238 This represents methods where CherryPy will automatically try to
239 read the request body. WebOb lazily reads POST requests with the
240 correct content type, and no other bodies.
246 As ``req.environ['SERVER_PROTOCOL']``
249 As ``req.query_string``
252 ``remote.ip`` is like ``req.remote_addr``. ``remote.port`` is not
253 available. ``remote.name`` is in
254 ``req.environ.get('REMOTE_HOST')``
260 A method that is somewhat similar to ``req.get_response()``.
269 As ``req.environ['SERVER_PROTOCOL']``
289 From information `from the wiki
290 <http://www.cherrypy.org/wiki/ResponseObject>`_.
293 This is an iterable in CherryPy, a string in WebOb;
294 ``res.app_iter`` gives an iterable in WebOb.
300 This turns a stream/iterator body into a single string. Accessing
301 ``res.body`` will do this automatically.
304 Accessible through ``res.set_cookie(...)``, ``res.delete_cookie``,
305 ``res.unset_cookie()``
311 In ``res.headerlist``
314 This can make CherryPy stream the response body out directory.
315 There is direct no equivalent; you can use a dynamically generated
316 iterator to do something similar.
327 `Yaro <http://lukearno.com/projects/yaro/>`_ is a small wrapper around
328 the WSGI environment, much like WebOb in scope.
330 The WebOb objects have many more methods and attributes. The Yaro
331 Response object is a much smaller subset of WebOb's Response.
343 A ``SimpleCookie`` object in Yaro; a dictionary in WebOb
344 (``SimpleCookie`` can represent cookie parameters, but cookie
345 parameters are only sent with responses not requests)
348 Returns a URI object, no equivalent (only string URIs available).
351 Not available (response-related). ``webob.exc.HTTPFound()`` can
354 ``forward(yaroapp)``, ``wsgi_forward(wsgiapp)``:
355 Available with ``req.get_response(app)`` and
356 ``req.call_application(app)``. In both cases it is a WSGI
357 application in WebOb, there is no special kind of communication;
358 ``req.call_application()`` just returns a ``webob.Response`` object.
361 The request object in WebOb *may* have a ``req.response``
367 Probably not that many people know about this library, which is a
368 offshoot of `Pocoo <http://pocoo.org>`_, and used to go by another
369 name (Columbrid?) This library is based around WSGI, similar to Paste
372 This is take from the `wrapper documentation
373 <http://werkzeug.pocoo.org/documentation/wrappers>`_.
387 In ``req.POST`` (as FieldStorage objects)
395 In ``res.body`` (settable as ``res.body`` or ``res.app_iter``)
397 In ``res.status_int``
399 In ``res.content_type``
401 With ``res.body_file.write(data)``
406 From the Zope 3 interfaces for the `Request
407 <http://apidoc.zope.org/++apidoc++/Interface/zope.publisher.interfaces.browser.IBrowserRequest/index.html>`_
409 <http://apidoc.zope.org/++apidoc++/Interface/zope.publisher.interfaces.http.IHTTPResponse/index.html>`_.
414 ``locale``, ``setupLocale()``:
415 This is not fully calculated, but information is available in
416 ``req.accept_languages``.
418 ``principal``, ``setPrincipal(principal)``:
419 ``req.remote_user`` gives the username, but there is no standard
420 place for a user *object*.
422 ``publication``, ``setPublication()``,
423 These are associated with the object publishing system in Zope.
424 This kind of publishing system is outside the scope of WebOb.
426 ``traverse(object)``, ``getTraversalStack()``, ``setTraversalStack()``:
427 These all relate to traversal, which is part of the publishing
430 ``processInputs()``, ``setPathSuffix(steps)``:
431 Also associated with traversal and preparing the request.
440 This is the security context for the request; all the possible
441 participants or principals in the request. There's no
445 Extra information associated with the request. This would
446 generally go in custom keys of ``req.environ``, or if you set
447 attributes those attributes are stored in
448 ``req.environ['webob.adhoc_attrs']``.
451 There is no standard debug flag for WebOb.
453 ``__getitem__(key)``, ``get(key)``, etc:
454 These treat the request like a dictionary, which WebOb does not
455 do. They seem to take values from the environment, not
456 parameters. Also on the Zope request object is ``items()``,
457 ``__contains__(key)``, ``__iter__()``, ``keys()``, ``__len__()``,
460 ``getPositionalArguments()``:
461 I'm not sure what the equivalent would be, as there are no
462 positional arguments during instantiation (it doesn't fit into
463 WSGI). Maybe ``wsgiorg.urlvars``?
465 ``retry()``, ``supportsRetry()``:
466 Creates a new request that can be used to retry a request.
467 Similar to ``req.copy()``.
469 ``close()``, ``hold(obj)``:
470 This closes resources associated with the request, including any
471 "held" objects. There's nothing similar.
477 Not sure what this is or does.
480 No direct equivalent; you'd have to do ``res.headers = [];
481 res.body = ''; res.status = 200``
483 ``setCookie(name, value, **kw)``:
484 Is ``res.set_cookie(...)``.
489 ``expireCookie(name)``:
490 Is ``res.delete_cookie(name)``.
492 ``appendToCookie(name, value)``:
493 This appends the value to any existing cookie (separating values
494 with a colon). WebOb does not do this.
496 ``setStatus(status)``:
497 Availble by setting ``res.status`` (can be set to an integer or a
498 string of "code reason").
500 ``getHeader(name, default=None)``:
501 Is ``res.headers.get(name)``.
504 Is ``res.status_int`` (or ``res.status`` to include reason)
506 ``addHeader(name, value)``:
507 Is ``res.headers.add(name, value)`` (in Zope and WebOb, this does
508 not clobber any previous value).
511 Is ``res.headerlist``.
513 ``setHeader(name, value)``:
514 Is ``res.headers[name] = value``.
516 ``getStatusString()``:
520 This consumes any non-string body to turn the body into a single
521 string. Any access to ``res.body`` will do this (e.g., when you
522 have set the ``res.app_iter``).
525 This is available with ``webob.exc.HTTP*()``.
527 ``handleException(exc_info)``:
528 This is provided with a tool like ``paste.exceptions``.
530 ``consumeBodyIter()``:
531 This returns the iterable for the body, even if the body was a
532 string. Anytime you access ``res.app_iter`` you will get an
533 iterable. ``res.body`` and ``res.app_iter`` can be interchanged
534 and accessed as many times as you want, unlike the Zope
537 ``setResult(result)``:
538 You can achieve the same thing through ``res.body = result``, or
539 ``res.app_iter = result``. ``res.body`` accepts None, a unicode
540 string (*if* you have set a charset) or a normal string.
541 ``res.app_iter`` only accepts None and an interable. You can't
542 update all of a response with one call.
544 Like in Zope, WebOb updates Content-Length. Unlike Zope, it does
545 not automatically calculate a charset.
551 Some key attributes from the `mod_python
552 <http://modpython.org/live/current/doc-html/pyapi-mprequest-mem.html>`_
562 In ``req.remote_user``.
564 ``req.get_remote_host()``:
565 In ``req.environ['REMOTE_ADDR']`` or ``req.remote_addr``.
567 ``req.headers_in.get('referer')``:
568 In ``req.headers.get('referer')`` or ``req.referer`` (same pattern
569 for other request headers, presumably).
574 ``util.redirect`` or ``req.status = apache.HTTP_MOVED_TEMPORARILY``:
578 from webob.exc import HTTPMovedTemporarily()
579 exc = HTTPMovedTemporarily(location=url)
580 return exc(environ, start_response)
582 ``req.content_type = "application/x-csv"`` and
583 ``req.headers_out.add('Content-Disposition', 'attachment;filename=somefile.csv'):
587 res = req.ResponseClass()
588 res.content_type = 'application/x-csv'
589 res.headers.add('Content-Disposition', 'attachment;filename=somefile.csv')
590 return res(environ, start_response)