Middleware¶
Middleware - это система хуков для обработки запросов/ответов в Django. Это легкая, низкоуровневая система «плагинов» для глобального изменения входных или выходных данных Django.
Каждый компонент промежуточного ПО отвечает за выполнение определенной функции. Например, Django включает компонент промежуточного ПО, AuthenticationMiddleware
, который связывает пользователей с запросами с помощью сессий.
Этот документ объясняет, как работает промежуточное ПО, как активировать промежуточное ПО и как написать свое собственное промежуточное ПО. Django поставляется с некоторым встроенным промежуточным ПО, которое вы можете использовать прямо из коробки. Они документированы в разделе built-in middleware reference.
Написание собственного промежуточного программного обеспечения¶
Фабрика промежуточного ПО - это вызываемая переменная, которая принимает вызываемую переменную 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
для последнего промежуточного модуля в цепочке не будет собственно представлением, а скорее методом-оберткой из обработчика, который позаботится о применении view middleware, вызове представления с соответствующими аргументами URL и применении template-response и exception промежуточных модулей.
Middleware может находиться в любом месте вашего пути Python.
__init__(get_response)
¶
Фабрики промежуточного ПО должны принимать аргумент get_response
. Вы также можете инициализировать некоторое глобальное состояние для промежуточного ПО. Помните о нескольких предостережениях:
- Django инициализирует ваш промежуточный модуль только аргументом
get_response
, поэтому вы не можете определить__init__()
как требующий каких-либо других аргументов. - В отличие от метода
__call__()
, который вызывается один раз на каждый запрос,__init__()
вызывается только один раз, когда запускается веб-сервер.
Пометить промежуточное ПО как неиспользуемое¶
Иногда полезно определить во время запуска, следует ли использовать часть промежуточного программного обеспечения. В таких случаях метод __init__()
вашего промежуточного ПО может поднять MiddlewareNotUsed
. Тогда Django удалит этот middleware из процесса middleware и запишет отладочное сообщение в логгер django.request, когда DEBUG
будет True
.
Активация промежуточного программного обеспечения¶
Чтобы активировать компонент промежуточного ПО, добавьте его в список MIDDLEWARE
в настройках Django.
В MIDDLEWARE
каждый компонент middleware представлен строкой: полный путь Python к классу или имени функции фабрики middleware. Например, вот значение по умолчанию, созданное 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()
не позволит любому представлению, запущенному после промежуточного программного обеспечения, получить доступ к modify the upload handlers for the request, и обычно его следует избегать.
Класс CsrfViewMiddleware
можно считать исключением, поскольку он предоставляет декораторы csrf_exempt()
и csrf_protect()
, которые позволяют представлениям явно контролировать, в какой момент должна происходить проверка CSRF.
process_exception()
¶
-
process_exception
(request, exception)¶
request
- это объект HttpRequest
. exception
- это объект Exception
, поднятый функцией view.
Django вызывает process_exception()
, когда представление вызывает исключение. process_exception()
должен вернуть либо None
, либо объект HttpResponse
. Если он возвращает объект HttpResponse
, то будет применен шаблонный ответ и промежуточное ПО ответа, и полученный ответ будет возвращен браузеру. В противном случае включается default exception handling.
Опять же, промежуточные программы запускаются в обратном порядке во время фазы ответа, которая включает 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-ответ с кодом статуса ошибки. Certain exceptions преобразуются в код состояния 4xx, в то время как неизвестное исключение преобразуется в код состояния 500.
Это преобразование происходит до и после каждого промежуточного ПО (можно считать, что это тонкая пленка между слоями лука), так что каждое промежуточное ПО всегда может рассчитывать на получение какого-либо HTTP-ответа после вызова его вызываемой переменной get_response
. Middleware не нужно беспокоиться о том, чтобы обернуть свой вызов get_response
в try/except
и обработать исключение, которое могло быть вызвано более поздним middleware или представлением. Даже если следующее промежуточное ПО в цепочке вызовет, например, исключение Http404
, ваше промежуточное ПО не увидит этого исключения; вместо этого оно получит объект HttpResponse
с status_code
из 404.
Обновление промежуточного программного обеспечения, существовавшего до версии 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
применяется только к исключениям, вызванным из представления (или изrender
методаTemplateResponse
). Исключения, вызванные из промежуточного ПО, преобразуются в соответствующий HTTP-ответ и затем передаются следующему промежуточному ПО. - При
MIDDLEWARE_CLASSES
, если методprocess_response
вызывает исключение, методыprocess_response
всех предыдущих промежуточных программ пропускаются, и всегда возвращается HTTP-ответ500 Internal Server Error
(даже если вызванное исключение было, например,Http404
). ПриMIDDLEWARE
исключение, поднятое промежуточным ПО, будет немедленно преобразовано в соответствующий HTTP-ответ, а затем следующее по порядку промежуточное ПО увидит этот ответ. Промежуточное ПО никогда не пропускается из-за того, что промежуточное ПО подняло исключение.