.. index::
   single: Bicking, Ian
   single: WebOb

.. _webob_chapter:

Request and Response Objects
============================

.. note:: This chapter is adapted from a portion of the :term:`WebOb`
   documentation, originally written by Ian Bicking.

:app:`Pyramid` uses the :term:`WebOb` package as a basis for its
:term:`request` and :term:`response` object implementations.  The
:term:`request` object that is passed to a :app:`Pyramid` :term:`view` is an
instance of the :class:`pyramid.request.Request` class, which is a subclass of
:class:`webob.Request`.  The :term:`response` returned from a :app:`Pyramid`
:term:`view` :term:`renderer` is an instance of the
:mod:`pyramid.response.Response` class, which is a subclass of the
:class:`webob.Response` class.  Users can also return an instance of
:class:`pyramid.response.Response` directly from a view as necessary.

WebOb is a project separate from :app:`Pyramid` with a separate set of authors
and a fully separate `set of documentation
<https://docs.pylonsproject.org/projects/webob/en/latest/index.html>`_.  :app:`Pyramid` adds some
functionality to the standard WebOb request, which is documented in the
:ref:`request_module` API documentation.

WebOb provides objects for HTTP requests and responses.  Specifically it does
this by wrapping the `WSGI <https://wsgi.readthedocs.io/en/latest/>`_ request
environment and response status, header list, and app_iter (body) values.

WebOb request and response objects provide many conveniences for parsing WSGI
requests and forming WSGI responses.  WebOb is a nice way to represent "raw"
WSGI requests and responses.  However, we won't cover that use case in this
document, as users of :app:`Pyramid` don't typically need to use the
WSGI-related features of WebOb directly.  The `reference documentation
<https://docs.pylonsproject.org/projects/webob/en/latest/reference.html>`_ shows many examples of
creating requests and using response objects in this manner, however.

.. index::
   single: request object
   single: request attributes

Request
~~~~~~~

The request object is a wrapper around the `WSGI environ dictionary
<https://www.python.org/dev/peps/pep-0333/#environ-variables>`_.  This
dictionary contains keys for each header, keys that describe the request
(including the path and query string), a file-like object for the request body,
and a variety of custom keys.  You can always access the environ with
``req.environ``.

Some of the most important and interesting attributes of a request object are
below.

``req.method``
    The request method, e.g., ``GET``, ``POST``

``req.GET``
    A :term:`multidict` with all the variables in the query string.

``req.POST``
    A :term:`multidict` with all the variables in the request body.  This only
    has variables if the request was a ``POST`` and it is a form submission.

``req.params``
    A :term:`multidict` with a combination of everything in ``req.GET`` and
    ``req.POST``.

``req.body``
    The contents of the body of the request.  This contains the entire request
    body as a string.  This is useful when the request is a ``POST`` that is
    *not* a form submission, or a request like a ``PUT``.  You can also get
    ``req.body_file`` for a file-like object.

``req.json_body``
    The JSON-decoded contents of the body of the request. See
    :ref:`request_json_body`.

``req.cookies``
    A simple dictionary of all the cookies.

``req.headers``
    A dictionary of all the headers.  This dictionary is case-insensitive.

``req.urlvars`` and ``req.urlargs``
    ``req.urlvars`` are the keyword parameters associated with the request URL.
    ``req.urlargs`` are the positional parameters. These are set by products
    like `Routes <https://routes.readthedocs.io/en/latest/>`_ and `Selector
    <https://github.com/lukearno/selector>`_.

Also for standard HTTP request headers, there are usually attributes such as
``req.accept_language``, ``req.content_length``, and ``req.user_agent``.  These
properties expose the *parsed* form of each header, for whatever parsing makes
sense.  For instance, ``req.if_modified_since`` returns a :mod:`datetime`
object (or None if the header is was not provided).

.. note:: Full API documentation for the :app:`Pyramid` request object is
   available in :ref:`request_module`.

.. index::
   single: request attributes (special)

.. _special_request_attributes:

Special Attributes Added to the Request by :app:`Pyramid`
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++

In addition to the standard :term:`WebOb` attributes, :app:`Pyramid` adds
special attributes to every request: ``context``, ``registry``, ``root``,
``subpath``, ``traversed``, ``view_name``, ``virtual_root``,
``virtual_root_path``, ``session``, ``matchdict``, and ``matched_route``. These
attributes are documented further within the :class:`pyramid.request.Request`
API documentation.

.. index::
   single: request URLs

URLs
++++

In addition to these attributes, there are several ways to get the URL of the
request and its parts.  We'll show various values for an example URL
``http://localhost/app/blog?id=10``, where the application is mounted at
``http://localhost/app``.

``req.url``
    The full request URL with query string, e.g.,
    ``http://localhost/app/blog?id=10``

``req.host``
    The host information in the URL, e.g., ``localhost``

``req.host_url``
    The URL with the host, e.g., ``http://localhost``

``req.application_url``
    The URL of the application (just the ``SCRIPT_NAME`` portion of the path,
    not ``PATH_INFO``), e.g., ``http://localhost/app``

``req.path_url``
    The URL of the application including the ``PATH_INFO``, e.g.,
    ``http://localhost/app/blog``

``req.path``
    The URL including ``PATH_INFO`` without the host or scheme, e.g.,
    ``/app/blog``

``req.path_qs``
    The URL including ``PATH_INFO`` and the query string, e.g,
    ``/app/blog?id=10``

``req.query_string``
    The query string in the URL, e.g., ``id=10``

``req.relative_url(url, to_application=False)``
    Gives a URL relative to the current URL.  If ``to_application`` is True,
    then resolves it relative to ``req.application_url``.

.. index::
   single: request methods

Methods
+++++++

There are methods of request objects documented in
:class:`pyramid.request.Request` but you'll find that you won't use very many
of them.  Here are a couple that might be useful:

``Request.blank(base_url)``
    Creates a new request with blank information, based at the given URL.  This
    can be useful for subrequests and artificial requests.  You can also use
    ``req.copy()`` to copy an existing request, or for subrequests
    ``req.copy_get()`` which copies the request but always turns it into a GET
    (which is safer to share for subrequests).

``req.get_response(wsgi_application)``
    This method calls the given WSGI application with this request, and returns
    a :class:`pyramid.response.Response` object.  You can also use this for
    subrequests or testing.

.. index::
   single: request (and text/unicode)
   single: unicode and text (and the request)

Text (Unicode)
++++++++++++++

Most of the properties of the request object will be text values.
The values in ``req.POST``, ``req.GET``, ``req.params``, and ``req.cookies`` will contain text and are generated assuming a UTF-8 charset.
The client *can* indicate the charset with something like ``Content-Type: application/x-www-form-urlencoded; charset=utf8``, but browsers seldom set this.
You can reset the charset of an existing request with ``newreq = req.decode('utf-8')``, or during instantiation with ``Request(environ, charset='utf8')``.

.. index::
   single: multidict (WebOb)

.. _multidict_narr:

Multidict
+++++++++

Several attributes of a WebOb request are multidict structures (such as
``request.GET``, ``request.POST``, and ``request.params``).  A multidict is a
dictionary where a key can have multiple values.  The quintessential example is
a query string like ``?pref=red&pref=blue``; the ``pref`` variable has two
values: ``red`` and ``blue``.

In a multidict, when you do ``request.GET['pref']``, you'll get back only
``"blue"`` (the last value of ``pref``).  This returned result might not be
expected—sometimes returning a string, and sometimes returning a list—and may
be cause of frequent exceptions.  If you want *all* the values back, use
``request.GET.getall('pref')``.  If you want to be sure there is *one and only
one* value, use ``request.GET.getone('pref')``, which will raise an exception
if there is zero or more than one value for ``pref``.

When you use operations like ``request.GET.items()``, you'll get back something
like ``[('pref', 'red'), ('pref', 'blue')]``.  All the key/value pairs will
show up.  Similarly ``request.GET.keys()`` returns ``['pref', 'pref']``. 
Multidict is a view on a list of tuples; all the keys are ordered, and all the
values are ordered.

API documentation for a multidict exists as
:class:`pyramid.interfaces.IMultiDict`.

.. index::
   pair: json_body; request

.. _request_json_body:

Dealing with a JSON-Encoded Request Body
++++++++++++++++++++++++++++++++++++++++

.. versionadded:: 1.1

:attr:`pyramid.request.Request.json_body` is a property that returns a
:term:`JSON`-decoded representation of the request body.  If the request does
not have a body, or the body is not a properly JSON-encoded value, an exception
will be raised when this attribute is accessed.

This attribute is useful when you invoke a :app:`Pyramid` view callable via,
for example, jQuery's ``$.ajax`` function, which has the potential to send a
request with a JSON-encoded body.

Using ``request.json_body`` is equivalent to:

.. code-block:: python

    from json import loads
    loads(request.body, encoding=request.charset)

Here's how to construct an AJAX request in JavaScript using :term:`jQuery` that
allows you to use the ``request.json_body`` attribute when the request is sent
to a :app:`Pyramid` application:

.. code-block:: javascript

    jQuery.ajax({type:'POST',
                 url: 'http://localhost:6543/', // the pyramid server
                 data: JSON.stringify({'a':1}),
                 contentType: 'application/json'});

When such a request reaches a view in your application, the
``request.json_body`` attribute will be available in the view callable body.

.. code-block:: python

    @view_config(renderer='string')
    def aview(request):
        print(request.json_body)
        return 'OK'

For the above view, printed to the console will be:

.. code-block:: python

    {'a': 1}

For bonus points, here's a bit of client-side code that will produce a request
that has a body suitable for reading via ``request.json_body`` using Python's
``urllib2`` instead of a JavaScript AJAX request:

.. code-block:: python

    import urllib2
    import json

    json_payload = json.dumps({'a':1})
    headers = {'Content-Type':'application/json'}
    req = urllib2.Request('http://localhost:6543/', json_payload, headers)
    resp = urllib2.urlopen(req)

If you are doing Cross-origin resource sharing (CORS), then the standard
requires the browser to do a pre-flight HTTP OPTIONS request. The easiest way
to handle this is to add an extra ``view_config`` for the same route, with
``request_method`` set to ``OPTIONS``, and set the desired response header
before returning. You can find examples of response headers `Access control
CORS, Preflighted requests
<https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests>`_.

.. index::
   single: cleaning up after request

.. _cleaning_up_after_a_request:

Cleaning up after a Request
+++++++++++++++++++++++++++

Sometimes it's required to perform some cleanup at the end of a request when a
database connection is involved.

For example, let's say you have a ``mypackage`` :app:`Pyramid` application
package that uses SQLAlchemy, and you'd like the current SQLAlchemy database
session to be removed after each request.  Put the following in the
``mypackage.__init__`` module:

.. code-block:: python
    :linenos:

    from mypackage.models import DBSession

    from pyramid.events import subscriber
    from pyramid.events import NewRequest

    def cleanup_callback(request):
        DBSession.remove()

    @subscriber(NewRequest)
    def add_cleanup_callback(event):
        event.request.add_finished_callback(cleanup_callback)

Registering the ``cleanup_callback`` finished callback at the start of a
request (by causing the ``add_cleanup_callback`` to receive a
:class:`pyramid.events.NewRequest` event at the start of each request) will
cause the DBSession to be removed whenever request processing has ended. Note
that in the example above, for the :class:`pyramid.events.subscriber` decorator
to work, the :meth:`pyramid.config.Configurator.scan` method must be called
against your ``mypackage`` package during application initialization.

.. note::
   This is only an example.  In particular, it is not necessary to cause
   ``DBSession.remove`` to be called in an application generated from the
   :app:`Pyramid` cookiecutter, because these all use the ``pyramid_tm`` package.
   The cleanup done by ``DBSession.remove`` is unnecessary when ``pyramid_tm``
   :term:`middleware` is configured into the application.

More Details
++++++++++++

More detail about the request object API is available as follows.

- :class:`pyramid.request.Request` API documentation

- `WebOb documentation <https://docs.pylonsproject.org/projects/webob/en/latest/index.html>`_.  All
  methods and attributes of a ``webob.Request`` documented within the WebOb
  documentation will work with request objects created by :app:`Pyramid`.

.. index::
   single: response object

Response
~~~~~~~~

The :app:`Pyramid` response object can be imported as
:class:`pyramid.response.Response`.  This class is a subclass of the
``webob.Response`` class.  The subclass does not add or change any
functionality, so the WebOb Response documentation will be completely relevant
for this class as well.

A response object has three fundamental parts:

``response.status``
    The response code plus reason message, like ``200 OK``.  To set the code
    without a message, use ``status_int``, i.e., ``response.status_int = 200``.

``response.headerlist``
    A list of all the headers, like ``[('Content-Type', 'text/html')]``.
    There's a case-insensitive :term:`multidict` in ``response.headers`` that
    also allows you to access these same headers.

``response.app_iter``
    An iterable (such as a list or generator) that will produce the content of
    the response.  This is also accessible as ``response.body`` (bytes),
    ``response.text`` (a Unicode string, informed by ``response.charset``), and
    ``response.body_file`` (a file-like object; writing to it appends to
    ``app_iter``).

Everything else in the object typically derives from this underlying state.
Here are some highlights:

``response.content_type``
    The content type *not* including the ``charset`` parameter.
    
    Typical use: ``response.content_type = 'text/html'``.

    Default value: ``response.content_type = 'text/html'``.

``response.charset``
    The ``charset`` parameter of the content-type, it also informs encoding in
    ``response.text``. ``response.content_type_params`` is a dictionary of all
    the parameters.

``response.set_cookie(name, value, max_age=None, path='/', ...)``
    Set a cookie.  The keyword arguments control the various cookie parameters.
    The ``max_age`` argument is the length for the cookie to live in seconds
    (you may also use a timedelta object).  The ``Expires`` key will also be
    set based on the value of ``max_age``.

``response.delete_cookie(name, path='/', domain=None)``
    Delete a cookie from the client.  This sets ``max_age`` to 0 and the cookie
    value to ``''``.

``response.cache_expires(seconds=0)``
    This makes the response cacheable for the given number of seconds, or if
    ``seconds`` is ``0`` then the response is uncacheable (this also sets the
    ``Expires`` header).

``response(environ, start_response)``
    The response object is a WSGI application.  As an application, it acts
    according to how you create it.  It *can* do conditional responses if you
    pass ``conditional_response=True`` when instantiating (or set that
    attribute later).  It can also do HEAD and Range requests.

.. index::
   single: response headers

Headers
+++++++

Like the request, most HTTP response headers are available as properties. These
are parsed, so you can do things like ``response.last_modified =
os.path.getmtime(filename)``.

The details are available in the :mod:`webob.response` API documentation.

.. index::
   single: response (creating)

Instantiating the Response
++++++++++++++++++++++++++

Of course most of the time you just want to *make* a response.  Generally any
attribute of the response can be passed in as a keyword argument to the class,
e.g.:

.. code-block:: python
    :linenos:

    from pyramid.response import Response
    response = Response(body='hello world!', content_type='text/plain')

The status defaults to ``'200 OK'``.

The value of ``content_type`` defaults to
``webob.response.Response.default_content_type``, which is ``text/html``. You
can subclass :class:`pyramid.response.Response` and set
``default_content_type`` to override this behavior.

.. index::
   single: exception responses

Exception Responses
+++++++++++++++++++

To facilitate error responses like ``404 Not Found``, the module
:mod:`pyramid.httpexceptions` contains classes for each kind of error response.
These include boring but appropriate error bodies.  The exceptions exposed by
this module, when used under :app:`Pyramid`, should be imported from the
:mod:`pyramid.httpexceptions` module.  This import location contains subclasses
and replacements that mirror those in the ``webob.exc`` module.

Each class is named ``pyramid.httpexceptions.HTTP*``, where ``*`` is the reason
for the error.  For instance, :class:`pyramid.httpexceptions.HTTPNotFound`
subclasses :class:`pyramid.response.Response`, so you can manipulate the
instances in the same way.  A typical example is:

.. code-block:: python
    :linenos:

    from pyramid.httpexceptions import HTTPNotFound
    from pyramid.httpexceptions import HTTPMovedPermanently

    response = HTTPNotFound('There is no such resource')
    # or:
    response = HTTPMovedPermanently(location=new_url)

More Details
++++++++++++

More details about the response object API are available in the
:mod:`pyramid.response` documentation.  More details about exception responses
are in the :mod:`pyramid.httpexceptions` API documentation.  The `WebOb
documentation <https://docs.pylonsproject.org/projects/webob/en/latest/index.html>`_ is also useful.
