This document covers all the details of the Request and Response
objects. It is written to be testable with doctest -- this
affects the flavor of the documentation, perhaps to its detriment.
But it also means you can feel confident that the documentation is
correct.

Note

All of the code samples below are for Python 3, and will not function as-is
on Python 2.

This is a somewhat different approach to reference documentation compared to
the extracted documentation for the request and
response.

(Note that the WSGI environment is a dictionary with a dozen required
keys, so it's a bit lengthly to show a complete example of what it
would look like -- usually your WSGI server will create it.)

The request object wraps the environment; it has very little
internal state of its own. Instead attributes you access read and
write to the environment dictionary.

You don't have to understand the details of WSGI to use this library;
this library handles those details for you. You also don't have to
use this exclusively of other libraries. If those other libraries
also keep their state in the environment, multiple wrappers can
coexist. Examples of libraries that can coexist include
paste.wsgiwrappers.Request
(used by Pylons) and yaro.Request.

The WSGI environment has a number of required variables. To make it
easier to test and play around with, the Request class has a
constructor that will fill in a minimal environment:

req.body is a file-like object that
gives the body of the request (e.g., a POST form, the body of a PUT, etc).
It's kind of boring to start, but you can set it to a string and that will be
turned into a file-like object. You can read the entire body with
req.body.

Requests can have variables in one of two locations: the query string
(?id=1), or in the body of the request (generally a POST form).
Note that even POST requests can have a query string, so both kinds of
variables can exist at the same time. Also, a variable can show up
more than once, as in ?check=a&check=b.

For these variables WebOb uses a MultiDict, which
is basically a dictionary wrapper on a list of key/value pairs. It looks like
a single-valued dictionary, but you can access all the values of a key with
.getall(key) (which always
returns a list, possibly an empty list). You also get all key/value pairs when
using .items() and all values with
.values().

We'll have to create a request body and change the method to get POST.
Until we do that, the variables are boring:

>>> req.POST<NoVars: Not a form request>>>> list(req.POST.items())# NoVars can be read like a dict, but not written[]>>> req.method='POST'>>> req.body=b'name=Joe&email=joe@example.com'>>> req.POSTMultiDict([('name', 'Joe'), ('email', 'joe@example.com')])>>> req.POST['name']'Joe'

Often you won't care where the variables come from. (Even if you care about
the method, the location of the variables might not be important.) There is a
dictionary called req.params that
contains variables from both sources:

Submissions are by default UTF-8, you can force a different character set by
setting the charset on the Request object explicitly. A client can indicate
the character set with Content-Type:application/x-www-form-urlencoded;charset=utf8, but very few clients actually do this (sometimes XMLHttpRequest
requests will do this, as JSON is always UTF8 even when a page is served with a
different character set). You can force a charset, which will affect all the
variables:

The headers are all modifiable, as are other environmental variables (like
req.remote_user, which maps
to request.environ['REMOTE_USER']).

If you want to copy the request you can use req.copy(); this copies the environ dictionary, and
the request body from environ['wsgi.input'].

The method req.remove_conditional_headers(remove_encoding=True) can be used to remove
headers that might result in a 304NotModified response. If you are
writing some intermediary it can be useful to avoid these headers. Also if
remove_encoding is true (the default) then any Accept-Encoding header
will be removed, which can result in gzipped responses.

There are several request headers that tell the server what the client
accepts. These are accept (the Content-Type that is accepted),
accept_charset (the charset accepted), accept_encoding
(the Content-Encoding, like gzip, that is accepted), and
accept_language (generally the preferred language of the client).

The objects returned support containment to test for acceptability.
E.g.:

>>> 'text/html'inreq.acceptTrue

Because no header means anything is potentially acceptable, this is
returning True. We can set it to see more interesting behavior (the
example means that text/html is okay, but
application/xhtml+xml is preferred):

There a number of ways to make a conditional request. A conditional
request is made when the client has a document, but it is not sure if
the document is up to date. If it is not, it wants a new version. If
the document is up to date then it doesn't want to waste the
bandwidth, and expects a 304NotModified response.

ETags are generally the best technique for these kinds of requests;
this is an opaque string that indicates the identity of the object.
For instance, it's common to use the mtime (last modified) of the file,
plus the number of bytes, and maybe a hash of the filename (if there's
a possibility that the same URL could point to two different
server-side filenames based on other variables). To test if a 304
response is appropriate, you can use:

For range requests there are two important headers, If-Range (which is
form of conditional request) and Range (which requests a range). If
the If-Range header fails to match then the full response (not a
range) should be returned:

Note that the range headers use inclusive ranges (the last byte
indexed is included), where Python always uses a range where the last
index is excluded from the range. The .stop index is in the
Python form.

Another kind of conditional request is a request (typically PUT) that
includes If-Match or If-Unmodified-Since. In this case you are saying
"here is an update to a resource, but don't apply it if someone else
has done something since I last got the resource". If-Match means "do
this if the current ETag matches the ETag I'm giving".
If-Unmodified-Since means "do this if the resource has remained
unchanged".

The request object can be used to make handy subrequests or test
requests against WSGI applications. If you want to make subrequests,
you should copy the request (with req.copy()) before sending it to
multiple applications, since applications might modify the request
when they are run.

The webob.Response object contains
everything necessary to make a WSGI response. Instances of it are in fact WSGI
applications, but it can also represent the result of calling a WSGI
application (as noted in Calling WSGI Applications). It can also be a way
of accumulating a response in your WSGI application.

A WSGI response is made up of a status (like 200OK), a list of
headers, and a body (or iterator that will produce a body).

The res.body attribute represents the entire body of the request
as a single string (not unicode, though you can set it to unicode if
you have a charset defined). There is also a res.app_iter
attribute that reprsents the body as an iterator. WSGI applications
return these app_iter iterators instead of strings, and sometimes
it can be problematic to load the entire iterator at once (for
instance, if it returns the contents of a very large file). Generally
it is not a problem, and often the iterator is something simple like a
one-item list containing a string with the entire body.

If you set the body then Content-Length will also be set, and an
res.app_iter will be created for you. If you set res.app_iter
then Content-Length will be cleared, but it won't be set for you.

There is also a file-like object you can access, which will update the
app_iter in-place (turning the app_iter into a list if necessary):

>>> # Used with a redirect:>>> res.location='http://localhost/foo'>>> # Indicates that the server accepts Range requests:>>> res.accept_ranges='bytes'>>> # Used by caching proxies to tell the client how old the>>> # response is:>>> res.age=120>>> # Show what methods the client can do; typically used in>>> # a 405 Method Not Allowed response:>>> res.allow=['GET','PUT']>>> # Set the cache-control header:>>> res.cache_control.max_age=360>>> res.cache_control.no_transform=True>>> # Tell the browser to treat the response as an attachment:>>> res.content_disposition='attachment; filename=foo.xml'>>> # Used if you had gzipped the body:>>> res.content_encoding='gzip'>>> # What language(s) are in the content:>>> res.content_language=['en']>>> # Seldom used header that tells the client where the content>>> # is from:>>> res.content_location='http://localhost/foo'>>> # Seldom used header that gives a hash of the body:>>> res.content_md5='big-hash'>>> # Means we are serving bytes 0-500 inclusive, out of 1000 bytes total:>>> # you can also use the range setter shown earlier>>> res.content_range=(0,501,1000)>>> # The length of the content; set automatically if you set>>> # res.body:>>> res.content_length=4>>> # Used to indicate the current date as the server understands>>> # it:>>> res.date=datetime.now()>>> # The etag:>>> res.etag='opaque-token'>>> # You can generate it from the body too:>>> res.md5_etag()>>> res.etag'1B2M2Y8AsgTpgAmY7PhCfg'>>> # When this page should expire from a cache (Cache-Control>>> # often works better):>>> importtime>>> res.expires=time.time()+60*60# 1 hour>>> # When this was last modified, of course:>>> res.last_modified=datetime(2007,1,1,12,0,tzinfo=UTC)>>> # Used with 503 Service Unavailable to hint the client when to>>> # try again:>>> res.retry_after=160>>> # Indicate the server software:>>> res.server='WebOb/1.0'>>> # Give a list of headers that the cache should vary on:>>> res.vary=['Cookie']

Note in each case you can general set the header to a string to avoid
any parsing, and set it to None to remove the header (or do something
like delres.vary).

In the case of date-related headers you can set the value to a
datetime instance (ideally with a UTC timezone), a time tuple, an
integer timestamp, or a properly-formatted string.

You can bind a request (or request WSGI environ) to the response
object. This is available through res.request or
res.environ. This is currently only used in setting
res.location, to make the location absolute if necessary.