mirror of
https://github.com/django/django.git
synced 2024-12-24 10:05:46 +00:00
9baf692a58
Thanks Tim Graham for polishing the patch, updating the tests, and writing documentation. Thanks Carl Meyer for shepherding the DEP.
332 lines
14 KiB
Plaintext
332 lines
14 KiB
Plaintext
==========
|
|
Middleware
|
|
==========
|
|
|
|
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
|
|
or output.
|
|
|
|
Each middleware component is responsible for doing some specific function. For
|
|
example, Django includes a middleware component,
|
|
:class:`~django.contrib.auth.middleware.AuthenticationMiddleware`, that
|
|
associates users with requests using sessions.
|
|
|
|
This document explains how middleware works, how you activate middleware, and
|
|
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
|
|
middleware reference </ref/middleware>`.
|
|
|
|
.. versionchanged:: 1.10
|
|
|
|
A new style of middleware was introduced for use with the new
|
|
:setting:`MIDDLEWARE` setting. If you're using the old
|
|
:setting:`MIDDLEWARE_CLASSES` setting, you'll need to :ref:`adapt old,
|
|
custom middleware <upgrading-middleware>` before using the new setting.
|
|
This document describes new-style middleware. Refer to this page in older
|
|
versions of the documentation for a description of how old-style middleware
|
|
works.
|
|
|
|
Writing your own middleware
|
|
===========================
|
|
|
|
A middleware factory is a callable that takes a ``get_response`` callable and
|
|
returns a middleware. A middleware is a callable that takes a request and
|
|
returns a response, just like a view.
|
|
|
|
A middleware can be written as a function that looks like this::
|
|
|
|
def simple_middleware(get_response):
|
|
# One-time configuration and initialization.
|
|
|
|
def middleware(request):
|
|
# Code to be executed for each request before
|
|
# the view is called.
|
|
|
|
try:
|
|
response = get_response(request)
|
|
except Exception as e:
|
|
# Code to handle an exception that wasn't caught
|
|
# further up the chain, if desired.
|
|
...
|
|
|
|
# Code to be executed for each request/response after
|
|
# the view is called.
|
|
|
|
return response
|
|
|
|
return middleware
|
|
|
|
Or it can be written as a class with a ``__call__()`` method, like this::
|
|
|
|
class SimpleMiddleware(object):
|
|
def __init__(self, get_response):
|
|
self.get_response = get_response
|
|
# One-time configuration and initialization.
|
|
|
|
def __call__(self, request):
|
|
# Code to be executed for each request before
|
|
# the view is called.
|
|
|
|
try:
|
|
response = self.get_response(request)
|
|
except Exception as e:
|
|
# Code to handle an exception that wasn't caught
|
|
# further up the chain, if desired.
|
|
...
|
|
|
|
# Code to be executed for each request/response after
|
|
# the view is called.
|
|
|
|
return response
|
|
|
|
In both examples, the ``try``/``except`` isn't required if the middleware
|
|
doesn't need to handle any exceptions. If it is included, it should probably
|
|
catch something more specific than ``Exception``.
|
|
|
|
The ``get_response`` callable provided by Django might be the actual view (if
|
|
this is the last listed middleware) or it might be the next middleware in the
|
|
chain. The current middleware doesn't need to know or care what exactly it is,
|
|
just that it represents whatever comes next.
|
|
|
|
The above is a slight simplification -- the ``get_response`` callable for the
|
|
last middleware in the chain won't be the actual view but rather a wrapper
|
|
method from the handler which takes care of applying :ref:`view middleware
|
|
<view-middleware>`, calling the view with appropriate URL arguments, and
|
|
applying :ref:`template-response <template-response-middleware>` middleware.
|
|
|
|
Middleware can live anywhere on your Python path.
|
|
|
|
``__init__(get_response)``
|
|
--------------------------
|
|
|
|
Middleware classes must accept a ``get_response`` argument. You can also
|
|
initialize some global state for the middleware. Keep in mind a couple of
|
|
caveats:
|
|
|
|
* Django initializes your middleware with only the ``get_response`` argument,
|
|
so you can't define ``__init__()`` as requiring any other arguments.
|
|
|
|
* Unlike the ``__call__()`` method which get called once per request,
|
|
``__init__()`` is called only *once*, when the Web server starts.
|
|
|
|
.. versionchanged:: 1.10
|
|
|
|
In older versions, ``__init__`` was not called until the Web server
|
|
responded to its first request.
|
|
|
|
If you want to allow your middleware to be used in Django 1.9 and earlier,
|
|
make ``get_response`` an optional argument (``get_response=None``).
|
|
|
|
Marking middleware as unused
|
|
----------------------------
|
|
|
|
It's sometimes useful to determine at startup time whether a piece of
|
|
middleware should be used. In these cases, your middleware's ``__init__()``
|
|
method may raise :exc:`~django.core.exceptions.MiddlewareNotUsed`. Django will
|
|
then remove that middleware from the middleware process and log a debug message
|
|
to the :ref:`django-request-logger` logger when :setting:`DEBUG` is ``True``.
|
|
|
|
Activating middleware
|
|
=====================
|
|
|
|
To activate a middleware component, add it to the :setting:`MIDDLEWARE` list in
|
|
your Django settings.
|
|
|
|
In :setting:`MIDDLEWARE`, each middleware component is represented by a string:
|
|
the full Python path to the middleware's class or function name. For example,
|
|
here's the default value created by :djadmin:`django-admin startproject
|
|
<startproject>`::
|
|
|
|
MIDDLEWARE = [
|
|
'django.middleware.security.SecurityMiddleware',
|
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
'django.middleware.common.CommonMiddleware',
|
|
'django.middleware.csrf.CsrfViewMiddleware',
|
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
'django.contrib.messages.middleware.MessageMiddleware',
|
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
]
|
|
|
|
A Django installation doesn't require any middleware — :setting:`MIDDLEWARE`
|
|
can be empty, if you'd like — but it's strongly suggested that you at least use
|
|
:class:`~django.middleware.common.CommonMiddleware`.
|
|
|
|
The order in :setting:`MIDDLEWARE` 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`. See
|
|
:ref:`middleware-ordering` for some common hints about ordering of Django
|
|
middleware classes.
|
|
|
|
Hooks and application order
|
|
===========================
|
|
|
|
During the request phase, before calling the view, Django applies middleware
|
|
in the order it's defined in :setting:`MIDDLEWARE`, top-down. You can think of
|
|
it like an onion: each middleware class is a "layer" that wraps the view.
|
|
|
|
Middleware see only the changes made by middleware that run before it. A
|
|
middleware (and the view) is skipped entirely if a preceding middleware
|
|
short-circuits by returning a response without ever calling ``get_response``.
|
|
That response will only pass through the middleware that have already run.
|
|
|
|
Similarly, a middleware that sees the request on the way in and doesn't return
|
|
a response is guaranteed that it will always see the response on the way back
|
|
out. If the middleware also wants to see any uncaught exception on the way out,
|
|
it can wrap its call to ``get_response()`` in a ``try``/``except``.
|
|
|
|
Besides the middleware pattern described earlier, you can add two other methods
|
|
to class-based middleware:
|
|
|
|
.. _view-middleware:
|
|
|
|
``process_view()``
|
|
------------------
|
|
|
|
.. method:: process_view(request, view_func, view_args, view_kwargs)
|
|
|
|
``request`` is an :class:`~django.http.HttpRequest` object. ``view_func`` is
|
|
the Python function that Django is about to use. (It's the actual function
|
|
object, not the name of the function as a string.) ``view_args`` is a list of
|
|
positional arguments that will be passed to the view, and ``view_kwargs`` is a
|
|
dictionary of keyword arguments that will be passed to the view. Neither
|
|
``view_args`` nor ``view_kwargs`` include the first view argument
|
|
(``request``).
|
|
|
|
``process_view()`` is called just before Django calls the view.
|
|
|
|
It should return either ``None`` or an :class:`~django.http.HttpResponse`
|
|
object. If it returns ``None``, Django will continue processing this request,
|
|
executing any other ``process_view()`` middleware and, then, the appropriate
|
|
view. If it returns an :class:`~django.http.HttpResponse` object, Django won't
|
|
bother calling the appropriate view; it'll apply response middleware to that
|
|
:class:`~django.http.HttpResponse` and return the result.
|
|
|
|
.. note::
|
|
|
|
Accessing :attr:`request.POST <django.http.HttpRequest.POST>` inside
|
|
middleware before the view runs or in ``process_view()`` will prevent any
|
|
view running after the middleware from being able to :ref:`modify the
|
|
upload handlers for the request <modifying_upload_handlers_on_the_fly>`,
|
|
and should normally be avoided.
|
|
|
|
The :class:`~django.middleware.csrf.CsrfViewMiddleware` class can be
|
|
considered an exception, as it provides the
|
|
:func:`~django.views.decorators.csrf.csrf_exempt` and
|
|
:func:`~django.views.decorators.csrf.csrf_protect` decorators which allow
|
|
views to explicitly control at what point the CSRF validation should occur.
|
|
|
|
.. _template-response-middleware:
|
|
|
|
``process_template_response()``
|
|
-------------------------------
|
|
|
|
.. method:: process_template_response(request, response)
|
|
|
|
``request`` is an :class:`~django.http.HttpRequest` object. ``response`` is
|
|
the :class:`~django.template.response.TemplateResponse` object (or equivalent)
|
|
returned by a Django view or by a middleware.
|
|
|
|
``process_template_response()`` is called just after the view has finished
|
|
executing, if the response instance has a ``render()`` method, indicating that
|
|
it is a :class:`~django.template.response.TemplateResponse` or equivalent.
|
|
|
|
It must return a response object that implements a ``render`` method. It could
|
|
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.
|
|
|
|
You don't need to explicitly render responses -- responses will be
|
|
automatically rendered once all template response middleware has been
|
|
called.
|
|
|
|
Middleware are run in reverse order during the response phase, which
|
|
includes ``process_template_response()``.
|
|
|
|
Dealing with streaming responses
|
|
================================
|
|
|
|
Unlike :class:`~django.http.HttpResponse`,
|
|
:class:`~django.http.StreamingHttpResponse` does not have a ``content``
|
|
attribute. As a result, middleware can no longer assume that all responses
|
|
will have a ``content`` attribute. If they need access to the content, they
|
|
must test for streaming responses and adjust their behavior accordingly::
|
|
|
|
if response.streaming:
|
|
response.streaming_content = wrap_streaming_content(response.streaming_content)
|
|
else:
|
|
response.content = alter_content(response.content)
|
|
|
|
.. note::
|
|
|
|
``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
|
|
====================
|
|
|
|
A middleware that does some custom exception handling might looks like this::
|
|
|
|
class ExceptionMiddleware(object):
|
|
def __init__(self, get_response):
|
|
self.get_response = get_response
|
|
|
|
def __call__(self, request):
|
|
try:
|
|
response = self.get_response(request)
|
|
except Exception as e:
|
|
# Do something with the exception and possibly reraise it
|
|
# unless you wish to silence it.
|
|
...
|
|
return response
|
|
|
|
Middleware that wants to do something for all exception responses, an HTTP 404
|
|
for example, need to both catch the appropriate exception (e.g. ``Http404``)
|
|
and look for regular responses with the status code of interest. You can
|
|
subclass :class:`~django.middleware.exception.ExceptionMiddleware` if you want
|
|
to transform exceptions into the appropriate response.
|
|
|
|
.. _upgrading-middleware:
|
|
|
|
Upgrading pre-Django 1.10-style middleware
|
|
==========================================
|
|
|
|
.. class:: django.utils.deprecation.MiddlewareMixin
|
|
:module:
|
|
|
|
Django provides ``django.utils.deprecation.MiddlewareMixin`` to ease providing
|
|
the existing built-in middleware in both new-style and old-style forms and to
|
|
ease similar conversions of third-party middleware.
|
|
|
|
In most cases, this mixin will be sufficient to convert a middleware with
|
|
sufficient backwards-compatibility; the new short-circuiting semantics will be
|
|
harmless or even beneficial to the existing middleware.
|
|
|
|
In a few cases, a middleware class may need more invasive changes to adjust to
|
|
the new semantics.
|
|
|
|
For example, in the current request-handling logic, the handler transforms any
|
|
exception that passes through all ``process_exception`` middleware uncaught
|
|
into a response with appropriate status code (e.g. 404, 403, 400, or 500), and
|
|
then passes that response through the full chain of ``process_response``
|
|
middleware.
|
|
|
|
In new-style middleware, a given middleware only gets one shot at a given
|
|
response or uncaught exception "on the way out," and will see either a returned
|
|
response or an uncaught exception, but not both.
|
|
|
|
This means that certain middleware which want to do something with all 404
|
|
responses (for example, the ``RedirectFallbackMiddleware`` and
|
|
``FlatpageFallbackMiddleware`` in ``django.contrib.redirects`` and
|
|
``django.contrib.flatpages``) now need to watch out for both a 404 response
|
|
and an uncaught ``Http404`` exception. They do this by subclassing
|
|
:class:`~django.middleware.exception.ExceptionMiddleware`.
|