Промежуточное ПО
Промежуточное ПО — это фреймворк хуков в обработке запросов/ответов Django. Это лёгкая, низкоуровневая система «плагинов» для глобального изменения входных или выходных данных Django.
Каждый компонент промежуточного ПО отвечает за выполнение определённой функции. Например, Django включает компонент промежуточного ПО, AuthenticationMiddleware, который связывает пользователей с запросами, используя сессии.
Этот документ объясняет, как работает промежуточное ПО, как его активировать и как написать собственное промежуточное ПО. Django поставляется с некоторым встроенным промежуточным ПО, которым можно воспользоваться сразу. Оно документировано в справочнике по встроенному промежуточному ПО.
Написание собственного промежуточного ПО
Фабрика промежуточного ПО — это вызываемый объект, который принимает вызываемый объект get_response и возвращает промежуточное ПО. Промежуточное ПО — это вызываемый объект, который принимает запрос и возвращает ответ, как и представление.
Промежуточное ПО можно написать как функцию, которая выглядит так:
def simple_middleware(get_response):
# One-time configuration and initialization.
def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
return middleware
Или его можно написать как класс, экземпляры которого вызываемы, как это:
class SimpleMiddleware:
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 (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
Вызываемый объект get_response , предоставленный Django, может быть фактическим представлением (если это последний указанный компонент промежуточного ПО) или следующим компонентом промежуточного ПО в цепочке. Текущему компоненту промежуточного ПО не нужно знать или заботиться о том, что это именно, а только о том, что это представляет собой то, что следует дальше.
Вышеприведённое — упрощение. Вызываемый объект get_response для последнего компонента промежуточного ПО в цепочке не будет фактическим представлением, а скорее методом-обёрткой из обработчика, который отвечает за применение промежуточного ПО представления, вызов представления с соответствующими аргументами URL и применение промежуточного ПО ответа шаблона и исключений.
Промежуточное ПО может поддерживать только синхронный Python (по умолчанию), только асинхронный Python или оба. Подробности о том, как объявлять вашу поддержку и узнавать, какой запрос вы получаете, см. в Раздел асинхронной поддержки.
Промежуточное ПО может располагаться где угодно на вашем пути Python.
__init__(get_response)
Фабрики промежуточного ПО должны принимать аргумент get_response. Вы также можете инициализировать некоторое глобальное состояние для компонента промежуточного ПО. Имейте в виду несколько нюансов:
- Django инициализирует ваше промежуточное ПО только с аргументом
get_response, поэтому вы не можете определить__init__()как требующее какие-либо другие аргументы. - В отличие от метода
__call__(), который вызывается один раз на запрос,__init__()вызывается только один раз, при запуске веб-сервера.
Отмечание промежуточного ПО как неиспользуемого
Иногда полезно определить в момент запуска, должно ли использоваться промежуточное ПО. В таких случаях метод __init__() вашего компонента промежуточного ПО может вызвать MiddlewareNotUsed. Тогда Django удалит этот компонент промежуточного ПО из процесса и выведет сообщение отладки в логгер django.request, когда DEBUG имеет значение True.
Активация промежуточного ПО
Чтобы активировать компонент промежуточного ПО, добавьте его в список MIDDLEWARE в файлах настроек Django.
В MIDDLEWARE каждый компонент промежуточного ПО представлен строкой: полным путём Python к классу или имени функции фабрики промежуточного ПО. Например, вот значение по умолчанию, созданное django-admin
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",
]
Установка Django не требует никакого промежуточного ПО — MIDDLEWARE может быть пустым, если вам это нужно, — но настоятельно рекомендуется использовать хотя бы CommonMiddleware.
Порядок в MIDDLEWARE важен, потому что один компонент промежуточного ПО может зависеть от другого. Например, AuthenticationMiddleware сохраняет аутентифицированного пользователя в сессии; поэтому он должен выполняться после SessionMiddleware. См. Порядок компонентов промежуточного ПО для некоторых общих подсказок по порядку классов промежуточного ПО Django.
Порядок и слои промежуточного ПО
Во время фазы запроса, перед вызовом представления, Django применяет компоненты промежуточного ПО в порядке, определённом в MIDDLEWARE, сверху вниз.
Вы можете представить это как лук: каждый класс промежуточного ПО — это «слой», который оборачивает представление, которое находится в сердцевине лука. Если запрос проходит через все слои лука (каждый вызывает get_response для передачи запроса в следующий слой), до представления в ядре, ответ затем пройдёт через каждый слой (в обратном порядке) по пути наружу.
Если один из слоёв решает прервать выполнение и вернуть ответ, не вызывая свой метод get_response, ни один из слоёв лука внутри этого слоя (включая представление) не увидит запрос или ответ. Ответ будет возвращён только через те же слои, через которые прошёл запрос.
Другие хуки промежуточного ПО
Помимо описанного выше базового шаблона промежуточного ПО запроса/ответа, вы можете добавить три других специальных метода в классы промежуточного ПО:
process_view()
-
process_view(request, view_func, view_args, view_kwargs)
request — это объект HttpRequest. view_func — это функция Python, которую Django собирается использовать. (Это фактический объект функции, а не имя функции в виде строки.) view_args — это список позиционных аргументов, которые будут переданы представлению, а view_kwargs — это словарь ключевых аргументов, которые будут переданы представлению. Ни view_args, ни view_kwargs не включают первый аргумент представления (request).
process_view() вызывается непосредственно перед тем, как Django вызывает представление.
Он должен вернуть либо None, либо объект HttpResponse. Если он возвращает None, Django продолжит обработку этого запроса, выполняя любое другое промежуточное ПО process_view() и затем соответствующее представление. Если он возвращает объект HttpResponse, Django не станет вызывать соответствующее представление; он применит промежуточное ПО ответа к этому объекту HttpResponse и вернёт результат.
Примечание
Обращение к request.POST внутри промежуточного ПО до выполнения представления или в методе process_view() предотвратит возможность любого представления, выполняемого после промежуточного ПО, изменять обработчики загрузки для запроса, и обычно следует избегать этого.
Класс CsrfViewMiddleware можно рассматривать как исключение, так как он предоставляет декораторы csrf_exempt() и csrf_protect(), которые позволяют представлениям явно контролировать, в какой момент должна выполняться проверка CSRF.
process_exception()
-
process_exception(request, exception)
request — это объект HttpRequest. exception — это объект Exception , поднятый функцией представления.
Django вызывает process_exception() при возникновении исключения в представлении. process_exception() должен вернуть либо None, либо объект HttpResponse. Если он возвращает объект HttpResponse, шаблон ответа и промежуточное ПО ответа будут применены, и полученный ответ будет возвращён браузеру. В противном случае срабатывает стандартная обработка исключений.
Опять же, обработчики промежуточного слоя выполняются в обратном порядке во время фазы ответа, что включает process_exception. Если обработчик исключений возвращает ответ, методы process_exception классов обработчиков промежуточного слоя, расположенных выше этого обработчика, вообще не будут вызваны.
process_template_response()
-
process_template_response(request, response)
request — это объект HttpRequest. response — это объект TemplateResponse (или эквивалент), возвращаемый представлением Django или обработчиком промежуточного слоя.
process_template_response() вызывается сразу после завершения выполнения представления, если экземпляр ответа имеет метод render(), указывающий, что это TemplateResponse или эквивалент.
Он должен возвращать объект ответа, который реализует метод render. Он может изменить предоставленный response путём изменения response.template_name и response.context_data, или он может создать и вернуть совершенно новый TemplateResponse или эквивалент.
Вам не нужно явно рендерить ответы — ответы будут автоматически рендериться после вызова всех обработчиков промежуточного слоя для ответа.
Обработчики промежуточного слоя выполняются в обратном порядке во время фазы ответа, что включает process_template_response().
Работа с потоковыми ответами
В отличие от HttpResponse, StreamingHttpResponse не имеет атрибута content. В результате обработчики промежуточного слоя больше не могут предполагать, что все ответы будут иметь атрибут content. Если им нужен доступ к содержимому, они должны проверить потоковый ответ и соответствующим образом скорректировать своё поведение:
if response.streaming:
response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
response.content = alter_content(response.content)
Примечание
streaming_content следует считать слишком большим для хранения в памяти. Обработчики промежуточного слоя ответа могут обернуть его в новый генератор, но не должны его потреблять. Обычно обертывание реализуется следующим образом:
def wrap_streaming_content(content):
for chunk in content:
yield alter_content(chunk)
StreamingHttpResponse поддерживает как синхронные, так и асинхронные итераторы. Функция обертывания должна соответствовать. Проверьте StreamingHttpResponse.is_async, если вашему обработчику промежуточного слоя нужно поддерживать оба типа итераторов.
Добавлена поддержка потоковых ответов с асинхронными итераторами.
Обработка исключений
Django автоматически преобразует исключения, поднятые представлением или обработчиками промежуточного слоя, в соответствующий HTTP-ответ с кодом состояния ошибки. Определённые исключения преобразуются в коды состояния 4xx, а неизвестное исключение — в код состояния 500.
Это преобразование происходит до и после каждого обработчика промежуточного слоя (можно представить это как тонкую плёнку между слоями лука), так что каждый обработчик промежуточного слоя всегда может полагаться на получение какого-либо HTTP-ответа от вызова своего get_response вызываемого объекта. Обработчикам промежуточного слоя не нужно беспокоиться об обертывании своего вызова get_response в try/except и обработке исключения, которое могло быть поднято последующим обработчиком промежуточного слоя или представлением. Даже если следующий обработчик промежуточного слоя в цепочке поднимет исключение Http404, например, ваш обработчик промежуточного слоя не увидит это исключение; вместо этого он получит объект HttpResponse с кодом состояния status_code 404.
Вы можете установить DEBUG_PROPAGATE_EXCEPTIONS в значение True для пропуска этого преобразования и распространения исключений вверх.
Поддержка асинхронных обработчиков
Обработчики промежуточного слоя могут поддерживать любую комбинацию синхронных и асинхронных запросов. Django будет адаптировать запросы к требованиям обработчика промежуточного слоя, если он не может поддерживать оба, но с потерей производительности.
По умолчанию Django предполагает, что ваш обработчик промежуточного слоя способен обрабатывать только синхронные запросы. Чтобы изменить эти предположения, установите следующие атрибуты в вашей фабричной функции или классе обработчика промежуточного слоя:
-
sync_capable— логическое значение, указывающее, может ли обработчик промежуточного слоя обрабатывать синхронные запросы. По умолчаниюTrue. -
async_capable— логическое значение, указывающее, может ли обработчик промежуточного слоя обрабатывать асинхронные запросы. По умолчаниюFalse.
Если у вашего обработчика промежуточного слоя есть как sync_capable = True, так и async_capable = True, Django передаст запрос без его преобразования. В этом случае вы можете выяснить, получит ли ваш обработчик промежуточного слоя асинхронный запрос, проверив, является ли переданный объект get_response функцией-генератором, используя asgiref.sync.iscoroutinefunction.
Модуль django.utils.decorators содержит декораторы sync_only_middleware(), async_only_middleware() и sync_and_async_middleware(), которые позволяют применять эти флаги к фабричным функциям обработчиков промежуточного слоя.
Возвращаемый вызываемый объект должен соответствовать синхронной или асинхронной природе метода get_response. Если у вас есть асинхронный get_response, вы должны вернуть функцию-генератор (async def).
Методы process_view, process_template_response и process_exception, если они предоставлены, также должны быть адаптированы для соответствия режиму синхрон/асинхрон. Однако Django будет индивидуально адаптировать их по мере необходимости, если вы этого не сделаете, что приведёт к дополнительной потере производительности.
Вот пример того, как создать функцию обработчика промежуточного слоя, поддерживающую оба режима:
from asgiref.sync import iscoroutinefunction
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 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
Примечание
Если вы объявляете гибридный обработчик промежуточного слоя, который поддерживает как синхронные, так и асинхронные вызовы, тип вызова, который вы получаете, может не соответствовать основному представлению. Django оптимизирует стек вызовов обработчиков промежуточного слоя, чтобы минимизировать переходы синхрон/асинхрон.
Таким образом, даже если вы оборачиваете асинхронное представление, вы можете быть вызваны в синхронном режиме, если между вами и представлением находятся другие синхронные обработчики промежуточного слоя.
При использовании асинхронного обработчика промежуточного слоя на основе класса необходимо убедиться, что экземпляры правильно помечены как функции-генераторы:
from asgiref.sync import iscoroutinefunction, markcoroutinefunction
class AsyncMiddleware:
async_capable = True
sync_capable = False
def __init__(self, get_response):
self.get_response = get_response
if iscoroutinefunction(self.get_response):
markcoroutinefunction(self)
async def __call__(self, request):
response = await self.get_response(request)
# Some logic ...
return response
Модернизация обработчиков промежуточного слоя по старой стилистике (предыдущие Django 1.10)
-
class django.utils.deprecation.MiddlewareMixin
Django предоставляет django.utils.deprecation.MiddlewareMixin для облегчения создания классов обработчиков промежуточного слоя, совместимых как с MIDDLEWARE, так и со старой системой MIDDLEWARE_CLASSES, и поддерживающих синхронные и асинхронные запросы. Все классы обработчиков промежуточного слоя, поставляемые с Django, совместимы с обоими вариантами настроек.
Миксин предоставляет метод __init__(), требующий аргумент get_response и сохраняющий его в self.get_response.
Метод __call__():
- Вызывает
self.process_request(request)(если определён). - Вызывает
self.get_response(request)для получения ответа от последующих обработчиков промежуточного слоя и представления. - Вызывает
self.process_response(request, response)(если определён). - Возвращает ответ.
Если используется с MIDDLEWARE_CLASSES, метод __call__() никогда не будет использован; Django вызывает process_request() и process_response() напрямую.
В большинстве случаев наследование от этого миксина будет достаточно для обеспечения совместимости старого обработчика промежуточного слоя с новой системой с достаточной обратной совместимостью. Новые короткие логики обхода будут безопасными или даже полезными для существующих обработчиков промежуточного слоя. В некоторых случаях классу обработчика промежуточного слоя могут потребоваться изменения для адаптации к новой логике.
Вот различия в поведении при использовании MIDDLEWARE и MIDDLEWARE_CLASSES:
- Под
MIDDLEWARE_CLASSES, каждый промежуточный обработчик всегда будет вызывать свой методprocess_response, даже если предыдущий промежуточный обработчик прервал выполнение, вернув ответ из своего методаprocess_request. СогласноMIDDLEWARE, промежуточные обработчики ведут себя как слои лука: слои, через которые проходит ответ при выходе, — это те же слои, которые обрабатывали запрос при входе. Если промежуточный обработчик прерывает выполнение, то только этот промежуточный обработчик и те, что перед ним вMIDDLEWARE, увидят ответ. - Под
MIDDLEWARE_CLASSES,process_exceptionприменяется к исключениям, возникшим в методе промежуточного обработчикаprocess_request. СогласноMIDDLEWARE,process_exceptionприменяется только к исключениям, возникшим в представлении (или в методеrenderTemplateResponse). Исключение, возникшее в промежуточном обработчике, преобразуется в соответствующий HTTP-ответ и передаётся следующему промежуточному обработчику. - Под
MIDDLEWARE_CLASSES, если методprocess_responseвызывает исключение, методыprocess_responseвсех предыдущих промежуточных обработчиков пропускаются, и всегда возвращается HTTP-ответ500 Internal Server Error(даже если возникшее исключение, например,Http404). СогласноMIDDLEWARE, исключение, возникшее в промежуточном обработчике, немедленно преобразуется в соответствующий HTTP-ответ, а затем следующий промежуточный обработчик увидит этот ответ. Промежуточные обработчики никогда не пропускаются из-за возникновения исключения в промежуточном обработчике.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.0/topics/http/middleware/