ПОСРЕДНИК
Посредник — это фреймворк хуков для обработки запросов/ответов 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)
Обработка исключений
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, который вам передаётся, функцией корутины, используя asyncio.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 будет индивидуально адаптировать их по мере необходимости, если вы этого не сделаете, что повлечёт дополнительные потери производительности.
Вот пример того, как создать функцию среднего компонента, которая поддерживает оба:
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
Примечание
Если вы объявляете гибридный средний компонент, который поддерживает как синхронные, так и асинхронные вызовы, тип вызова, который вы получите, может не соответствовать базовому представлению. Django оптимизирует стек вызовов среднего компонента, чтобы минимизировать переходы синхронность/асинхронность.
Таким образом, даже если вы оборачиваете асинхронное представление, вы можете быть вызваны в синхронном режиме, если между вами и представлением есть другие, синхронные средние компоненты.
Обновление средних компонентов старого стиля (до Django 1.10)
-
class django.utils.deprecation.MiddlewareMixin
Django предоставляет django.utils.deprecation.MiddlewareMixin для облегчения создания классов средних компонентов, совместимых как с MIDDLEWARE, так и со старым MIDDLEWARE_CLASSES, и поддерживающих синхронные и асинхронные запросы. Все классы средних компонентов, включённые в Django, совместимы с обоими наборами настроек.
Mixin предоставляет метод __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().
В большинстве случаев наследование от этого mixin будет достаточно, чтобы сделать средний компонент старого стиля совместимым с новой системой с достаточной обратной совместимостью. Новая семантика короткого замыкания будет безвредной или даже полезной для существующих средних компонентов. В некоторых случаях классу среднего компонента могут потребоваться изменения для адаптации к новой семантике.
Вот различия в поведении при использовании 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/4.0/topics/http/middleware/