summaryrefslogtreecommitdiff
path: root/docs/topics/http/middleware.txt
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2020-02-12 15:15:00 -0700
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-03-18 19:59:12 +0100
commitfc0fa72ff4cdbf5861a366e31cb8bbacd44da22d (patch)
treed419ce531586808b0a111664907b859cb6d22862 /docs/topics/http/middleware.txt
parent3f7e4b16bf58f99c71570ba75dc97db8265071be (diff)
Fixed #31224 -- Added support for asynchronous views and middleware.
This implements support for asynchronous views, asynchronous tests, asynchronous middleware, and an asynchronous test client.
Diffstat (limited to 'docs/topics/http/middleware.txt')
-rw-r--r--docs/topics/http/middleware.txt87
1 files changed, 85 insertions, 2 deletions
diff --git a/docs/topics/http/middleware.txt b/docs/topics/http/middleware.txt
index d72d39de5e..3fe00b947f 100644
--- a/docs/topics/http/middleware.txt
+++ b/docs/topics/http/middleware.txt
@@ -71,6 +71,10 @@ method from the handler which takes care of applying :ref:`view middleware
applying :ref:`template-response <template-response-middleware>` and
:ref:`exception <exception-middleware>` middleware.
+Middleware can either support only synchronous Python (the default), only
+asynchronous Python, or both. See :ref:`async-middleware` for details of how to
+advertise what you support, and know what kind of request you are getting.
+
Middleware can live anywhere on your Python path.
``__init__(get_response)``
@@ -282,6 +286,81 @@ if the very next middleware in the chain raises an
that exception; instead it will get an :class:`~django.http.HttpResponse`
object with a :attr:`~django.http.HttpResponse.status_code` of 404.
+.. _async-middleware:
+
+Asynchronous support
+====================
+
+.. versionadded:: 3.1
+
+Middleware can support any combination of synchronous and asynchronous
+requests. Django will adapt requests to fit the middleware's requirements if it
+cannot support both, but at a performance penalty.
+
+By default, Django assumes that your middleware is capable of handling only
+synchronous requests. To change these assumptions, set the following attributes
+on your middleware factory function or class:
+
+* ``sync_capable`` is a boolean indicating if the middleware can handle
+ synchronous requests. Defaults to ``True``.
+
+* ``async_capable`` is a boolean indicating if the middleware can handle
+ asynchronous requests. Defaults to ``False``.
+
+If your middleware has both ``sync_capable = True`` and
+``async_capable = True``, then Django will pass it the request in whatever form
+it is currently in. You can work out what type of request you have by seeing
+if the ``get_response`` object you are passed is a coroutine function or not
+(using :py:func:`asyncio.iscoroutinefunction`).
+
+The ``django.utils.decorators`` module contains
+:func:`~django.utils.decorators.sync_only_middleware`,
+:func:`~django.utils.decorators.async_only_middleware`, and
+:func:`~django.utils.decorators.sync_and_async_middleware` decorators that
+allow you to apply these flags to middleware factory functions.
+
+The returned callable must match the sync or async nature of the
+``get_response`` method. If you have an asynchronous ``get_response``, you must
+return a coroutine function (``async def``).
+
+``process_view``, ``process_template_response`` and ``process_exception``
+methods, if they are provided, should also be adapted to match the sync/async
+mode. However, Django will individually adapt them as required if you do not,
+at an additional performance penalty.
+
+Here's an example of how to detect and adapt your middleware if it supports
+both::
+
+ import asyncio
+ from django.utils.decorators import sync_and_async_middleware
+
+ @sync_and_async_middleware
+ def simple_middleware(get_response):
+ # One-time configuration and initialization goes here.
+ if asyncio.iscoroutinefunction(get_response):
+ async def middleware(request):
+ # Do something here!
+ response = await get_response(request)
+ return response
+
+ else:
+ def middleware(request):
+ # Do something here!
+ response = get_response(request)
+ return response
+
+ return middleware
+
+.. note::
+
+ If you declare a hybrid middleware that supports both synchronous and
+ asynchronous calls, the kind of call you get may not match the underlying
+ view. Django will optimize the middleware call stack to have as few
+ sync/async transitions as possible.
+
+ Thus, even if you are wrapping an async view, you may be called in sync
+ mode if there is other, synchronous middleware between you and the view.
+
.. _upgrading-middleware:
Upgrading pre-Django 1.10-style middleware
@@ -292,8 +371,8 @@ Upgrading pre-Django 1.10-style middleware
Django provides ``django.utils.deprecation.MiddlewareMixin`` to ease creating
middleware classes that are compatible with both :setting:`MIDDLEWARE` and the
-old ``MIDDLEWARE_CLASSES``. All middleware classes included with Django
-are compatible with both settings.
+old ``MIDDLEWARE_CLASSES``, and support synchronous and asynchronous requests.
+All middleware classes included with Django are compatible with both settings.
The mixin provides an ``__init__()`` method that requires a ``get_response``
argument and stores it in ``self.get_response``.
@@ -345,3 +424,7 @@ These are the behavioral differences between using :setting:`MIDDLEWARE` and
HTTP response, and then the next middleware in line will see that
response. Middleware are never skipped due to a middleware raising an
exception.
+
+.. versionchanged:: 3.1
+
+ Support for asynchronous requests was added to the ``MiddlewareMixin``.