Edited the middleware doc for completeness, clarity, and consistency.

This commit is contained in:
Aymeric Augustin 2012-12-09 22:22:36 +01:00
parent ae8e97384b
commit be9f2919e0
2 changed files with 113 additions and 69 deletions

View File

@ -134,7 +134,7 @@ class BaseHandler(object):
raise ValueError("The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name)) raise ValueError("The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name))
# If the response supports deferred rendering, apply template # If the response supports deferred rendering, apply template
# response middleware and the render the response # response middleware and then render the response
if hasattr(response, 'render') and callable(response.render): if hasattr(response, 'render') and callable(response.render):
for middleware_method in self._template_response_middleware: for middleware_method in self._template_response_middleware:
response = middleware_method(request, response) response = middleware_method(request, response)

View File

@ -4,25 +4,28 @@ Middleware
Middleware is a framework of hooks into Django's request/response processing. Middleware is a framework of hooks into Django's request/response processing.
It's a light, low-level "plugin" system for globally altering Django's input It's a light, low-level "plugin" system for globally altering Django's input
and/or output. or output.
Each middleware component is responsible for doing some specific function. For Each middleware component is responsible for doing some specific function. For
example, Django includes a middleware component, ``XViewMiddleware``, that adds example, Django includes a middleware component,
an ``"X-View"`` HTTP header to every response to a ``HEAD`` request. :class:`~django.middleware.transaction.TransactionMiddleware`, that wraps the
processing of each HTTP request in a database transaction.
This document explains how middleware works, how you activate middleware, and This document explains how middleware works, how you activate middleware, and
how to write your own middleware. Django ships with some built-in middleware how to write your own middleware. Django ships with some built-in middleware
you can use right out of the box; they're documented in the :doc:`built-in you can use right out of the box. They're documented in the :doc:`built-in
middleware reference </ref/middleware>`. middleware reference </ref/middleware>`.
Activating middleware Activating middleware
===================== =====================
To activate a middleware component, add it to the :setting:`MIDDLEWARE_CLASSES` To activate a middleware component, add it to the
list in your Django settings. In :setting:`MIDDLEWARE_CLASSES`, each middleware :setting:`MIDDLEWARE_CLASSES` tuple in your Django settings.
component is represented by a string: the full Python path to the middleware's
class name. For example, here's the default :setting:`MIDDLEWARE_CLASSES` In :setting:`MIDDLEWARE_CLASSES`, each middleware component is represented by
created by :djadmin:`django-admin.py startproject <startproject>`:: a string: the full Python path to the middleware's class name. For example,
here's the default value created by :djadmin:`django-admin.py startproject
<startproject>`::
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
@ -32,12 +35,33 @@ created by :djadmin:`django-admin.py startproject <startproject>`::
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
) )
During the request phases (:meth:`process_request` and :meth:`process_view`), A Django installation doesn't require any middleware —
Django applies middleware in the order it's defined in :setting:`MIDDLEWARE_CLASSES` can be empty, if you'd like — but it's strongly
:setting:`MIDDLEWARE_CLASSES`, top-down. During the response phases suggested that you at least use
(:meth:`process_template_response`, :meth:`process_response`, and :class:`~django.middleware.common.CommonMiddleware`.
:meth:`process_exception`), the classes are applied in reverse order, from the
bottom up. The order in :setting:`MIDDLEWARE_CLASSES` matters because a middleware can
depend on other middleware. For instance,
:class:`~django.contrib.auth.middleware.AuthenticationMiddleware` stores the
authenticated user in the session; therefore, it must run after
:class:`~django.contrib.sessions.middleware.SessionMiddleware`.
Hooks and application order
===========================
During the request phase, before calling the view, Django applies middleware
in the order it's defined in :setting:`MIDDLEWARE_CLASSES`, top-down. Two
hooks are available:
* :meth:`process_request`
* :meth:`process_view`
During the response phase, after calling the view, middleware are applied in
reverse order, from the bottom up. Three hooks are available:
* :meth:`process_exception` (only if the view raised an exception)
* :meth:`process_template_response` (only for template responses)
* :meth:`process_response`
.. image:: _images/middleware.svg .. image:: _images/middleware.svg
:alt: middleware application order :alt: middleware application order
@ -47,10 +71,7 @@ bottom up.
If you prefer, you can also think of it like an onion: each middleware class If you prefer, you can also think of it like an onion: each middleware class
is a "layer" that wraps the view. is a "layer" that wraps the view.
A Django installation doesn't require any middleware -- e.g., The behavior of each hook is described below.
:setting:`MIDDLEWARE_CLASSES` can be empty, if you'd like -- but it's strongly
suggested that you at least use
:class:`~django.middleware.common.CommonMiddleware`.
Writing your own middleware Writing your own middleware
=========================== ===========================
@ -65,16 +86,19 @@ Python class that defines one or more of the following methods:
.. method:: process_request(self, request) .. method:: process_request(self, request)
``request`` is an :class:`~django.http.HttpRequest` object. This method is ``request`` is an :class:`~django.http.HttpRequest` object.
called on each request, before Django decides which view to execute.
``process_request()`` should return either ``None`` or an ``process_request()`` is called on each request, before Django decides which
:class:`~django.http.HttpResponse` object. If it returns ``None``, Django will view to execute.
continue processing this request, executing any other middleware and, then, the
appropriate view. If it returns an :class:`~django.http.HttpResponse` object, It should return either ``None`` or an :class:`~django.http.HttpResponse`
Django won't bother calling ANY other request, view or exception middleware, or object. If it returns ``None``, Django will continue processing this request,
the appropriate view; it'll return that :class:`~django.http.HttpResponse`. executing any other ``process_request()`` middleware, then, ``process_view()``
Response middleware is always called on every response. middleware, and finally, the appropriate view. If it returns an
:class:`~django.http.HttpResponse` object, Django won't bother calling any
other request, view or exception middleware, or the appropriate view; it'll
apply response middleware to that :class:`~django.http.HttpResponse`, and
return the result.
.. _view-middleware: .. _view-middleware:
@ -91,14 +115,15 @@ dictionary of keyword arguments that will be passed to the view. Neither
``view_args`` nor ``view_kwargs`` include the first view argument ``view_args`` nor ``view_kwargs`` include the first view argument
(``request``). (``request``).
``process_view()`` is called just before Django calls the view. It should ``process_view()`` is called just before Django calls the view.
return either ``None`` or an :class:`~django.http.HttpResponse` object. If it
returns ``None``, Django will continue processing this request, executing any It should return either ``None`` or an :class:`~django.http.HttpResponse`
other ``process_view()`` middleware and, then, the appropriate view. If it object. If it returns ``None``, Django will continue processing this request,
returns an :class:`~django.http.HttpResponse` object, Django won't bother executing any other ``process_view()`` middleware and, then, the appropriate
calling ANY other request, view or exception middleware, or the appropriate view. If it returns an :class:`~django.http.HttpResponse` object, Django won't
view; it'll return that :class:`~django.http.HttpResponse`. Response bother calling any other view or exception middleware, or the appropriate
middleware is always called on every response. view; it'll apply response middleware to that
:class:`~django.http.HttpResponse`, and return the result.
.. note:: .. note::
@ -122,19 +147,17 @@ middleware is always called on every response.
.. method:: process_template_response(self, request, response) .. method:: process_template_response(self, request, response)
``request`` is an :class:`~django.http.HttpRequest` object. ``response`` is a ``request`` is an :class:`~django.http.HttpRequest` object. ``response`` is
subclass of :class:`~django.template.response.SimpleTemplateResponse` (e.g. the :class:`~django.template.response.TemplateResponse` object (or equivalent)
:class:`~django.template.response.TemplateResponse`) or any response object returned by a Django view or by a middleware.
that implements a ``render`` method.
``process_template_response()`` must return a response object that implements a ``process_template_response()`` is called just after the view has finished
``render`` method. It could alter the given ``response`` by changing executing, if the response instance has a ``render()`` method, indicating that
``response.template_name`` and ``response.context_data``, or it could create it is a :class:`~django.template.response.TemplateResponse` or equivalent.
and return a brand-new
:class:`~django.template.response.SimpleTemplateResponse` or equivalent.
``process_template_response()`` will only be called if the response It must return a response object that implements a ``render`` method. It could
instance has a ``render()`` method, indicating that it is a alter the given ``response`` by changing ``response.template_name`` and
``response.context_data``, or it could create and return a brand-new
:class:`~django.template.response.TemplateResponse` or equivalent. :class:`~django.template.response.TemplateResponse` or equivalent.
You don't need to explicitly render responses -- responses will be You don't need to explicitly render responses -- responses will be
@ -142,7 +165,7 @@ automatically rendered once all template response middleware has been
called. called.
Middleware are run in reverse order during the response phase, which Middleware are run in reverse order during the response phase, which
includes process_template_response. includes ``process_template_response()``.
.. _response-middleware: .. _response-middleware:
@ -151,21 +174,34 @@ includes process_template_response.
.. method:: process_response(self, request, response) .. method:: process_response(self, request, response)
``request`` is an :class:`~django.http.HttpRequest` object. ``response`` is the ``request`` is an :class:`~django.http.HttpRequest` object. ``response`` is
:class:`~django.http.HttpResponse` object returned by a Django view. the :class:`~django.http.HttpResponse` or
:class:`~django.http.StreamingHttpResponse` object returned by a Django view
or by a middleware.
``process_response()`` must return an :class:`~django.http.HttpResponse` ``process_response()`` is called on all responses before they're returned to
object. It could alter the given ``response``, or it could create and return a the browser.
brand-new :class:`~django.http.HttpResponse`.
It must return an :class:`~django.http.HttpResponse` or
:class:`~django.http.StreamingHttpResponse` object. It could alter the given
``response``, or it could create and return a brand-new
:class:`~django.http.HttpResponse` or
:class:`~django.http.StreamingHttpResponse`.
Unlike the ``process_request()`` and ``process_view()`` methods, the Unlike the ``process_request()`` and ``process_view()`` methods, the
``process_response()`` method is always called, even if the ``process_request()`` ``process_response()`` method is always called, even if the
and ``process_view()`` methods of the same middleware class were skipped because ``process_request()`` and ``process_view()`` methods of the same middleware
an earlier middleware method returned an :class:`~django.http.HttpResponse` class were skipped (because an earlier middleware method returned an
(this means that your ``process_response()`` method cannot rely on setup done in :class:`~django.http.HttpResponse`). In particular, this means that your
``process_request()``, for example). In addition, during the response phase the ``process_response()`` method cannot rely on setup done in
classes are applied in reverse order, from the bottom up. This means classes ``process_request()``.
defined at the end of :setting:`MIDDLEWARE_CLASSES` will be run first.
Finally, remember that during the response phase, middleware are applied in
reverse order, from the bottom up. This means classes defined at the end of
:setting:`MIDDLEWARE_CLASSES` will be run first.
Dealing with streaming responses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionchanged:: 1.5 .. versionchanged:: 1.5
``response`` may also be an :class:`~django.http.StreamingHttpResponse` ``response`` may also be an :class:`~django.http.StreamingHttpResponse`
@ -180,10 +216,17 @@ must test for streaming responses and adjust their behavior accordingly::
if response.streaming: if response.streaming:
response.streaming_content = wrap_streaming_content(response.streaming_content) response.streaming_content = wrap_streaming_content(response.streaming_content)
else: else:
response.content = wrap_content(response.content) response.content = alter_content(response.content)
``streaming_content`` should be assumed to be too large to hold in memory. .. note::
Middleware may wrap it in a new generator, but must not consume it.
``streaming_content`` should be assumed to be too large to hold in memory.
Response middleware may wrap it in a new generator, but must not consume
it. Wrapping is typically implemented as follows::
def wrap_streaming_content(content)
for chunk in content:
yield alter_content(chunk)
.. _exception-middleware: .. _exception-middleware:
@ -198,8 +241,9 @@ Middleware may wrap it in a new generator, but must not consume it.
Django calls ``process_exception()`` when a view raises an exception. Django calls ``process_exception()`` when a view raises an exception.
``process_exception()`` should return either ``None`` or an ``process_exception()`` should return either ``None`` or an
:class:`~django.http.HttpResponse` object. If it returns an :class:`~django.http.HttpResponse` object. If it returns an
:class:`~django.http.HttpResponse` object, the response will be returned to :class:`~django.http.HttpResponse` object, the template response and response
the browser. Otherwise, default exception handling kicks in. middleware will be applied, and the resulting response returned to the
browser. Otherwise, default exception handling kicks in.
Again, middleware are run in reverse order during the response phase, which Again, middleware are run in reverse order during the response phase, which
includes ``process_exception``. If an exception middleware returns a response, includes ``process_exception``. If an exception middleware returns a response,
@ -224,9 +268,9 @@ Marking middleware as unused
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It's sometimes useful to determine at run-time whether a piece of middleware It's sometimes useful to determine at run-time whether a piece of middleware
should be used. In these cases, your middleware's ``__init__`` method may raise should be used. In these cases, your middleware's ``__init__`` method may
``django.core.exceptions.MiddlewareNotUsed``. Django will then remove that raise :exc:`django.core.exceptions.MiddlewareNotUsed`. Django will then remove
piece of middleware from the middleware process. that piece of middleware from the middleware process.
Guidelines Guidelines
---------- ----------