1Differences Between WebOb and Other Systems 2+++++++++++++++++++++++++++++++++++++++++++ 3 4This document points out some of the API differences between the 5Request and Response object, and the objects in other systems. 6 7.. contents:: 8 9paste.wsgiwrappers and Pylons 10============================= 11 12The Pylons ``request`` and ``response`` object are based on 13``paste.wsgiwrappers.WSGIRequest`` and ``WSGIResponse`` 14 15There is no concept of ``defaults`` in WebOb. In Paste/Pylons these 16serve as threadlocal settings that control certain policies on the 17request and response object. In WebOb you should make your own 18subclasses to control policy (though in many ways simply being 19explicit elsewhere removes the need for this policy). 20 21Request 22------- 23 24``body``: 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`` 28 29``languages()``: 30 This is available through ``req.accept_language``, particularly 31 ``req.accept_language.best_match(supported_languages)`` 32 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)`` 37 38``errors``: 39 This controls how unicode decode errors are handled; it is now 40 named ``unicode_errors`` 41 42There are also many extra methods and attributes on WebOb Request 43objects. 44 45Response 46-------- 47 48``determine_charset()``: 49 Is now available as ``res.charset`` 50 51``has_header(header)``: 52 Should be done with ``header in res.headers`` 53 54``get_content()`` and ``wsgi_response()``: 55 These are gone; you should use ``res.body`` or ``res(environ, 56 start_response)`` 57 58``write(content)``: 59 Available in ``res.body_file.write(content)``. 60 61``flush()`` and ``tell()``: 62 Not available. 63 64There are also many extra methods and attributes on WebOb Response 65objects. 66 67Django 68====== 69 70This is a quick summary from reading `the Django documentation 71<http://www.djangoproject.com/documentation/request_response/>`_. 72 73Request 74------- 75 76``encoding``: 77 Is ``req.charset`` 78 79``REQUEST``: 80 Is ``req.params`` 81 82``FILES``: 83 File uploads are ``cgi.FieldStorage`` objects directly in 84 ``res.POST`` 85 86``META``: 87 Is ``req.environ`` 88 89``user``: 90 No equivalent (too connected to application model for WebOb). 91 There is ``req.remote_user``, which is only ever a string. 92 93``session``: 94 No equivalent 95 96``raw_post_data``: 97 Available with ``req.body`` 98 99``__getitem__(key)``: 100 You have to use ``req.params`` 101 102``is_secure()``: 103 No equivalent; you could use ``req.scheme == 'https'``. 104 105QueryDict 106--------- 107 108QueryDict is the way Django represents the multi-key dictionary-like 109objects that are request variables (query string and POST body 110variables). The equivalent in WebOb is MultiDict. 111 112Mutability: 113 WebOb dictionaries are sometimes mutable (req.GET is, 114 req.params is not) 115 116Ordering: 117 I believe Django does not order the keys fully; MultiDict is a 118 full ordering. Methods that iterate over the parameters iterate 119 over keys in their order in the original request. 120 121``keys()``, ``items()``, ``values()`` (plus ``iter*``): 122 These return all values in MultiDict, but only the last value for 123 a QueryDict. That is, given ``a=1&a=2`` with MultiDict 124 ``d.items()`` returns ``[('a', '1'), ('a', '2')]``, but QueryDict 125 returns ``[('a', '1')]`` 126 127``getlist(key)``: 128 Available as ``d.getall(key)`` 129 130``setlist(key)``: 131 No direct equivalent 132 133``appendlist(key, value)``: 134 Available as ``d.add(key, value)`` 135 136``setlistdefault(key, default_list)``: 137 No direct equivalent 138 139``lists()``: 140 Is ``d.dict_of_lists()`` 141 142The MultiDict object has a ``d.getone(key)`` method, that raises 143KeyError if there is not exactly one key. There is a method 144``d.mixed()`` which returns a version where values are lists *if* 145there are multiple values for a list. This is similar to how many 146cgi-based request forms are represented. 147 148Response 149-------- 150 151Constructor: 152 Somewhat different. WebOb takes any keyword arguments as 153 attribute assignments. Django only takes a couple arguments. The 154 ``mimetype`` argument is ``content_type``, and ``content_type`` is 155 the entire ``Content-Type`` header (including charset). 156 157dictionary-like: 158 The Django response object is somewhat dictionary-like, setting 159 headers. The equivalent dictionary-like object is 160 ``res.headers``. In WebOb this is a MultiDict. 161 162``has_header(header)``: 163 Use ``header in res.headers`` 164 165``flush()``, ``tell()``: 166 Not available 167 168``content``: 169 Use ``res.body`` for the ``str`` value, ``res.text`` for 170 the ``unicode`` value 171 172Response Subclasses 173------------------- 174 175These are generally like ``webob.exc`` objects. 176``HttpResponseNotModified`` is ``HTTPNotModified``; this naming 177translation generally works. 178 179CherryPy/TurboGears 180=================== 181 182The `CherryPy request object 183<http://www.cherrypy.org/wiki/RequestObject>`_ is also used by 184TurboGears 1.x. 185 186Request 187------- 188 189``app``: 190 No equivalent 191 192``base``: 193 ``req.application_url`` 194 195``close()``: 196 No equivalent 197 198``closed``: 199 No equivalent 200 201``config``: 202 No equivalent 203 204``cookie``: 205 A ``SimpleCookie`` object in CherryPy; a dictionary in WebOb 206 (``SimpleCookie`` can represent cookie parameters, but cookie 207 parameters are only sent with responses not requests) 208 209``dispatch``: 210 No equivalent (this is the object dispatcher in CherryPy). 211 212``error_page``, ``error_response``, ``handle_error``: 213 No equivalent 214 215``get_resource()``: 216 Similar to ``req.get_response(app)`` 217 218``handler``: 219 No equivalent 220 221``headers``, ``header_list``: 222 The WSGI environment represents headers as a dictionary, available 223 through ``req.headers`` (no list form is available in the request). 224 225``hooks``: 226 No equivalent 227 228``local``: 229 No equivalent 230 231``methods_with_bodies``: 232 This represents methods where CherryPy will automatically try to 233 read the request body. WebOb lazily reads POST requests with the 234 correct content type, and no other bodies. 235 236``namespaces``: 237 No equivalent 238 239``protocol``: 240 As ``req.environ['SERVER_PROTOCOL']`` 241 242``query_string``: 243 As ``req.query_string`` 244 245``remote``: 246 ``remote.ip`` is like ``req.remote_addr``. ``remote.port`` is not 247 available. ``remote.name`` is in 248 ``req.environ.get('REMOTE_HOST')`` 249 250``request_line``: 251 No equivalent 252 253``respond()``: 254 A method that is somewhat similar to ``req.get_response()``. 255 256``rfile``: 257 ``req.body_file`` 258 259``run``: 260 No equivalent 261 262``server_protocol``: 263 As ``req.environ['SERVER_PROTOCOL']`` 264 265``show_tracebacks``: 266 No equivalent 267 268``throw_errors``: 269 No equivalent 270 271``throws``: 272 No equivalent 273 274``toolmaps``: 275 No equivalent 276 277``wsgi_environ``: 278 As ``req.environ`` 279 280Response 281-------- 282 283From information `from the wiki 284<http://www.cherrypy.org/wiki/ResponseObject>`_. 285 286``body``: 287 This is an iterable in CherryPy, a string in WebOb; 288 ``res.app_iter`` gives an iterable in WebOb. 289 290``check_timeout``: 291 No equivalent 292 293``collapse_body()``: 294 This turns a stream/iterator body into a single string. Accessing 295 ``res.body`` will do this automatically. 296 297``cookie``: 298 Accessible through ``res.set_cookie(...)``, ``res.delete_cookie``, 299 ``res.unset_cookie()`` 300 301``finalize()``: 302 No equivalent 303 304``header_list``: 305 In ``res.headerlist`` 306 307``stream``: 308 This can make CherryPy stream the response body out directory. 309 There is direct no equivalent; you can use a dynamically generated 310 iterator to do something similar. 311 312``time``: 313 No equivalent 314 315``timed_out``: 316 No equivalent 317 318Yaro 319==== 320 321`Yaro <http://lukearno.com/projects/yaro/>`_ is a small wrapper around 322the WSGI environment, much like WebOb in scope. 323 324The WebOb objects have many more methods and attributes. The Yaro 325Response object is a much smaller subset of WebOb's Response. 326 327Request 328------- 329 330``query``: 331 As ``req.GET`` 332 333``form``: 334 As ``req.POST`` 335 336``cookie``: 337 A ``SimpleCookie`` object in Yaro; a dictionary in WebOb 338 (``SimpleCookie`` can represent cookie parameters, but cookie 339 parameters are only sent with responses not requests) 340 341``uri``: 342 Returns a URI object, no equivalent (only string URIs available). 343 344``redirect``: 345 Not available (response-related). ``webob.exc.HTTPFound()`` can 346 be useful here. 347 348``forward(yaroapp)``, ``wsgi_forward(wsgiapp)``: 349 Available with ``req.get_response(app)`` and 350 ``req.call_application(app)``. In both cases it is a WSGI 351 application in WebOb, there is no special kind of communication; 352 ``req.call_application()`` just returns a ``webob.Response`` object. 353 354``res``: 355 The request object in WebOb *may* have a ``req.response`` 356 attribute. 357 358Werkzeug 359======== 360 361An offshoot of `Pocoo <http://pocoo.org>`_, 362this library is based around WSGI, similar to Paste and Yaro. 363 364This is taken from the `wrapper documentation 365<http://werkzeug.pocoo.org/documentation/wrappers>`_. 366 367Request 368------- 369 370path: 371 As ``req.path_info`` 372args: 373 As ``req.GET`` 374form: 375 As ``req.POST`` 376values: 377 As ``req.params`` 378files: 379 In ``req.POST`` (as FieldStorage objects) 380data: 381 In ``req.body_file`` 382 383Response 384-------- 385 386response: 387 In ``res.body`` (settable as ``res.body`` or ``res.app_iter``) 388status: 389 In ``res.status_code`` 390mimetype: 391 In ``res.content_type`` 392 393Zope 3 394====== 395 396From the Zope 3 interfaces for the `Request 397<http://apidoc.zope.org/++apidoc++/Interface/zope.publisher.interfaces.browser.IBrowserRequest/index.html>`_ 398and `Response 399<http://apidoc.zope.org/++apidoc++/Interface/zope.publisher.interfaces.http.IHTTPResponse/index.html>`_. 400 401Request 402------- 403 404``locale``, ``setupLocale()``: 405 This is not fully calculated, but information is available in 406 ``req.accept_languages``. 407 408``principal``, ``setPrincipal(principal)``: 409 ``req.remote_user`` gives the username, but there is no standard 410 place for a user *object*. 411 412``publication``, ``setPublication()``, 413 These are associated with the object publishing system in Zope. 414 This kind of publishing system is outside the scope of WebOb. 415 416``traverse(object)``, ``getTraversalStack()``, ``setTraversalStack()``: 417 These all relate to traversal, which is part of the publishing 418 system. 419 420``processInputs()``, ``setPathSuffix(steps)``: 421 Also associated with traversal and preparing the request. 422 423``environment``: 424 In ``req.environ`` 425 426``bodyStream``: 427 In ``req.body_file`` 428 429``interaction``: 430 This is the security context for the request; all the possible 431 participants or principals in the request. There's no 432 equivalent. 433 434``annotations``: 435 Extra information associated with the request. This would 436 generally go in custom keys of ``req.environ``, or if you set 437 attributes those attributes are stored in 438 ``req.environ['webob.adhoc_attrs']``. 439 440``debug``: 441 There is no standard debug flag for WebOb. 442 443``__getitem__(key)``, ``get(key)``, etc: 444 These treat the request like a dictionary, which WebOb does not 445 do. They seem to take values from the environment, not 446 parameters. Also on the Zope request object is ``items()``, 447 ``__contains__(key)``, ``__iter__()``, ``keys()``, ``__len__()``, 448 ``values()``. 449 450``getPositionalArguments()``: 451 I'm not sure what the equivalent would be, as there are no 452 positional arguments during instantiation (it doesn't fit into 453 WSGI). Maybe ``wsgiorg.urlvars``? 454 455``retry()``, ``supportsRetry()``: 456 Creates a new request that can be used to retry a request. 457 Similar to ``req.copy()``. 458 459``close()``, ``hold(obj)``: 460 This closes resources associated with the request, including any 461 "held" objects. There's nothing similar. 462 463Response 464-------- 465 466``authUser``: 467 Not sure what this is or does. 468 469``reset()``: 470 No direct equivalent; you'd have to do ``res.headers = []; 471 res.body = ''; res.status = 200`` 472 473``setCookie(name, value, **kw)``: 474 Is ``res.set_cookie(...)``. 475 476``getCookie(name)``: 477 No equivalent. Hm. 478 479``expireCookie(name)``: 480 Is ``res.delete_cookie(name)``. 481 482``appendToCookie(name, value)``: 483 This appends the value to any existing cookie (separating values 484 with a colon). WebOb does not do this. 485 486``setStatus(status)``: 487 Availble by setting ``res.status`` (can be set to an integer or a 488 string of "code reason"). 489 490``getHeader(name, default=None)``: 491 Is ``res.headers.get(name)``. 492 493``getStatus()``: 494 Is ``res.status_code`` (or ``res.status`` to include reason) 495 496``addHeader(name, value)``: 497 Is ``res.headers.add(name, value)`` (in Zope and WebOb, this does 498 not clobber any previous value). 499 500``getHeaders()``: 501 Is ``res.headerlist``. 502 503``setHeader(name, value)``: 504 Is ``res.headers[name] = value``. 505 506``getStatusString()``: 507 Is ``res.status``. 508 509``consumeBody()``: 510 This consumes any non-string body to turn the body into a single 511 string. Any access to ``res.body`` will do this (e.g., when you 512 have set the ``res.app_iter``). 513 514``internalError()``: 515 This is available with ``webob.exc.HTTP*()``. 516 517``handleException(exc_info)``: 518 This is provided with a tool like ``paste.exceptions``. 519 520``consumeBodyIter()``: 521 This returns the iterable for the body, even if the body was a 522 string. Anytime you access ``res.app_iter`` you will get an 523 iterable. ``res.body`` and ``res.app_iter`` can be interchanged 524 and accessed as many times as you want, unlike the Zope 525 equivalents. 526 527``setResult(result)``: 528 You can achieve the same thing through ``res.body = result``, or 529 ``res.app_iter = result``. ``res.body`` accepts None, a unicode 530 string (*if* you have set a charset) or a normal string. 531 ``res.app_iter`` only accepts None and an interable. You can't 532 update all of a response with one call. 533 534 Like in Zope, WebOb updates Content-Length. Unlike Zope, it does 535 not automatically calculate a charset. 536 537 538mod_python 539========== 540 541Some key attributes from the `mod_python 542<http://modpython.org/live/current/doc-html/pyapi-mprequest-mem.html>`_ 543request object. 544 545Request 546------- 547 548``req.uri``: 549 In ``req.path``. 550 551``req.user``: 552 In ``req.remote_user``. 553 554``req.get_remote_host()``: 555 In ``req.environ['REMOTE_ADDR']`` or ``req.remote_addr``. 556 557``req.headers_in.get('referer')``: 558 In ``req.headers.get('referer')`` or ``req.referer`` (same pattern 559 for other request headers, presumably). 560 561Response 562-------- 563 564``util.redirect`` or ``req.status = apache.HTTP_MOVED_TEMPORARILY``: 565 566.. code-block:: python 567 568 from webob.exc import HTTPTemporaryRedirect 569 exc = HTTPTemporaryRedirect(location=url) 570 return exc(environ, start_response) 571 572``req.content_type = "application/x-csv"`` and 573``req.headers_out.add('Content-Disposition', 'attachment;filename=somefile.csv')``: 574 575.. code-block:: python 576 577 res = req.ResponseClass() 578 res.content_type = 'application/x-csv' 579 res.headers.add('Content-Disposition', 'attachment;filename=somefile.csv') 580 return res(environ, start_response) 581 582webapp Response 583=============== 584 585The Google App Engine `webapp 586<http://code.google.com/appengine/docs/python/tools/webapp/>`_ 587framework uses the WebOb Request object, but does not use its Response 588object. 589 590The constructor for ``webapp.Response`` does not take any arguments. 591The response is created by the framework, so you don't use it like 592``return Response(...)``, instead you use ``self.response``. Also the 593response object automatically has ``Cache-Control: no-cache`` set, 594while the WebOb response does not set any cache headers. 595 596``resp.set_status(code, message=None)``: 597 This is handled by setting the ``resp.status`` attribute. 598 599``resp.clear()``: 600 You'd do ``resp.body = ""`` 601 602``resp.wsgi_write(start_response)``: 603 This writes the response using the ``start_response`` callback, 604 and using the ``start_response`` writer. The WebOb response 605 object is called as a WSGI app (``resp(environ, start_response)``) 606 to do the equivalent. 607 608``resp.out.write(text)``: 609 This writes to an internal ``StringIO`` instance of the response. 610 This uses the ability of the standard StringIO object to hold 611 either unicode or ``str`` text, and so long as you are always 612 consistent it will encode your content (but it does not respect 613 your preferred encoding, it always uses UTF-8). The WebOb method 614 ``resp.write(text)`` is basically equivalent, and also accepts 615 unicode (using ``resp.charset`` for the encoding). You can also 616 write to ``resp.body_file``, but it does not allow unicode. 617 618Besides exposing a ``.headers`` attribute (based on 619`wsgiref.headers.Headers 620<http://docs.python.org/library/wsgiref.html#wsgiref.headers.Headers>`_) 621there is no other API for the webapp response object. This means the 622response lacks: 623 624* A usefully readable body or status. 625* A useful constructor that makes it easy to treat responses like 626 objects. 627* Providing a non-string ``app_iter`` for the body (like a generator). 628* Parsing of the Content-Type charset. 629* Getter/setters for parsed forms of headers, specifically 630 cache_control and last_modified. 631* The ``cache_expires`` method 632* ``set_cookie``, ``delete_cookie``, and ``unset_cookie``. Instead 633 you have to simply manually set the Set-Cookie header. 634* ``encode_content`` and ``decode_content`` for handling gzip encoding. 635* ``md5_etag()`` for generating an etag from the body. 636* Conditional responses that will return 304 based on the response and 637 request headers. 638* The ability to serve Range request automatically. 639 640PHP 641=== 642 643PHP does not have anything really resembling a request and response 644object. Instead these are encoded in a set of global objects for the 645request and functions for the response. 646 647``$_POST``, ``$_GET``, ``$_FILES`` 648---------------------------------- 649 650These represent ``req.POST`` and ``req.GET``. 651 652PHP uses the variable names to tell whether a variable can hold 653multiple values. For instance ``$_POST['name[]']``, which will be an 654array. In WebOb any variable can have multiple values, and you can 655get these through ``req.POST.getall('name')``. 656 657The files in ``$_FILES`` are simply in ``req.POST`` in WebOb, as 658FieldStorage instances. 659 660``$_COOKIES`` 661------------- 662 663This is in ``req.cookies``. 664 665``$_SERVER``, ``$_REQUEST``, ``$_ENV`` 666-------------------------------------- 667 668These are all in ``req.environ``. These are not split up like they 669are in PHP, it's all just one dictionary. Everything that would 670typically be in ``$_ENV`` is technically optional, and outside of a 671couple CGI-standard keys in ``$_SERVER`` most of those are also 672optional, but it is common for WSGI servers to populate the request 673with similar information as PHP. 674 675``$HTTP_RAW_POST_DATA`` 676----------------------- 677 678This contains the unparsed data in the request body. This is in 679``req.body``. 680 681The response 682------------ 683 684Response headers in PHP are sent with ``header("Header-Name: 685value")``. In WebOb there is a dictionary in ``resp.headers`` that 686can have values set; the headers aren't actually sent until you send 687the response. You can add headers without overwriting (the equivalent 688of ``header("...", false)``) with ``resp.headers.add('Header-Name', 689'value')``. 690 691The status in PHP is sent with ``http_send_status(code)``. In WebOb 692this is ``resp.status = code``. 693 694The body in PHP is sent implicitly through the rendering of the PHP 695body (or with ``echo`` or any other functions that send output). 696