Промежуточный слой (Middleware)¶
Промежуточный слой – это механизм «хуков» для обработки запросов и ответов в 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, вызываемый для последнего промежуточного программного обеспечения в цепочке, будет не фактическим представлением, а скорее методом-оболочкой обработчика, который заботится о применении промежуточного программного обеспечения представления <view-middleware>`, вызове представления с соответствующими аргументами URL и применении template-response и исключений промежуточное программное обеспечение.
Промежуточное ПО может поддерживать только синхронный Python (по умолчанию), только асинхронный Python или и то, и другое. См. Асинхронная поддержка, чтобы узнать, как рекламировать то, что вы поддерживаете, и узнать, какой тип запроса вы получаете.
Промежуточное программное обеспечение может находиться где угодно на вашем пути Python.
__init__¶
Фабрики промежуточного программного обеспечения должны принимать аргумент get_response. Вы также можете инициализировать некоторое глобальное состояние для промежуточного программного обеспечения. Имейте в виду пару предостережений:
Django инициализирует промежуточный слой без аргументов, поэтому метод
__init__не должен определять обязательные аргументы.В отличие от метода __call__(), который вызывается один раз для каждого запроса, __init__() вызывается только один раз при запуске веб-сервера.
Помечаем промежуточный слой как неиспользуемый¶
Иногда полезно в процессе работы приложения указать, что промежуточный слой не должен использоваться. В таком случае в методе __init__ можно вызывать исключение django.core.exceptions.MiddlewareNotUsed. Django исключит этот промежуточный слой из процесса обработки и запишет отладочное сообщение в логгер django.request, если параметр конфигурации DEBUG был установлен в True.
Подключение промежуточных слоёв¶
Чтобы подключить промежуточный слой, добавьте его в список MIDDLEWARE_CLASSES в настройках Django.
В параметре конфигурации MIDDLEWARE_CLASSES каждый промежуточный слой представлен строкой: полный путь для импорта класса промежуточного слоя. Например, вот значение настройки по умолчанию для проекта, который создается с помощью 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_CLASSES может быть пустым — но мы настоятельно рекомендуем использовать хотя бы CommonMiddleware.
Порядок MIDDLEWARE_CLASSES важен, т.к. один промежуточный слой может зависеть от другого. Например, AuthenticationMiddleware сохраняет текущего пользователя в сессии, поэтому должен срабатывать после SessionMiddleware. Смотрите советы, как лучше упорядочить промежуточные слои, в Порядок промежуточных слоёв.
Порядок и многоуровневое промежуточное программное обеспечение¶
Во время обработки запроса, перед вызовом представления, Django применяет промежуточные слои в порядке указанном в MIDDLEWARE_CLASSES, сверху вниз. Доступны два «хука»:
Вы можете думать об этом как о луковице: каждый класс промежуточного программного обеспечения представляет собой «слой», который обертывает представление, находящееся в ядре луковицы. Если запрос проходит через все уровни луковицы (каждый из них вызывает get_response для передачи запроса на следующий уровень) вплоть до представления в ядре, ответ затем пройдет через каждый уровень (в обратном порядке) на обратном пути.
Если один из уровней решит замкнуться и вернуть ответ, даже не вызывая его get_response, ни один из слоев лука внутри этого слоя (включая представление) не увидит запрос или ответ. Ответ будет возвращаться только через те же уровни, через которые прошел запрос.
Другие перехватчики промежуточного программного обеспечения¶
Помимо базового шаблона промежуточного программного обеспечения запросов/ответов, описанного ранее, вы можете добавить три других специальных метода к промежуточному программному обеспечению на основе классов:
process_view¶
- process_view(request, view_func, view_args, view_kwargs)¶
request – объект HttpRequest. view_func– функция представления, которую Django собирается вызвать для обработки запроса. (Это объект функции, а не название.) view_args – список позиционных аргументов, которые будут переданы в функцию представления, view_kwargs – словарь именованных аргументов. Ни view_args, ни view_kwargs не включают первый аргумент представления (request).
process_view() вызывается непосредственно перед вызовом представления.
Метод должен вернуть None или объект HttpResponse. Если вернул None, Django продолжит обработку запроса, вызывая process_view() других промежуточных слоёв, а затем представление. Если вернул объект HttpResponse, Django остановит обработку запроса, применит промежуточные слои для объекта HttpResponse, и вернет результат.
Примечание
Обращение к request.POST в промежуточном слое в методе process_request или 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, будет выполнена обработка ответа промежуточными слоями и результат отправится браузеру. Если вернул None, будет выполнена стандартная обработка исключений.
При обработке ответа промежуточные слои применяются в обратном порядке, это относится и к методу process_exception. Если этот метод вернул объект ответа, промежуточные слои выше будут пропущены.
process_template_response¶
- process_template_response(request, response)¶
request – объект HttpRequest. response – объект TemplateResponse (или аналогичный), который вернуло представление или промежуточный слой.
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/Exception и обработке исключения, которое могло быть вызвано более поздним промежуточным программным обеспечением или представлением. Даже если следующее промежуточное программное обеспечение в цепочке вызовет, например, исключение Http404, ваше промежуточное программное обеспечение не увидит это исключение; вместо этого он получит объект HttpResponse со значением status_code со значением 404.
You can set DEBUG_PROPAGATE_EXCEPTIONS to True to skip this
conversion and propagate exceptions upwards.
Асинхронная поддержка¶
Промежуточное программное обеспечение может поддерживать любую комбинацию синхронных и асинхронных запросов. Django адаптирует запросы в соответствии с требованиями промежуточного программного обеспечения, если оно не может поддерживать оба, но с потерей производительности.
По умолчанию Django предполагает, что ваше промежуточное ПО способно обрабатывать только синхронные запросы. Чтобы изменить эти предположения, установите следующие атрибуты в функции или классе фабрики промежуточного программного обеспечения:
sync_capable— это логическое значение, указывающее, может ли промежуточное программное обеспечение обрабатывать синхронные запросы. По умолчанию установленоTrue.async_capable— это логическое значение, указывающее, может ли промежуточное программное обеспечение обрабатывать асинхронные запросы. По умолчанию установлено значение «False».
If your middleware has both sync_capable = True and
async_capable = True, then Django will pass it the request without
converting it. In this case, you can work out if your middleware will receive
async requests by checking if the get_response object you are passed is a
coroutine function, using 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, совместимы с обеими настройками.
Миксин предоставляет метод __init__(), который требует аргумента get_response и сохраняет его в self.get_response.
Метод __call__():
Вызывает self.process_request(request) (если определено).
Вызывает self.get_response(request) для получения ответа от более позднего промежуточного программного обеспечения и представления.
Вызывает self.process_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. ВMIDDLEWAREprocess_Exceptionприменяется только к исключениям, возникшим из представления (или из методаrenderTemplateResponse). Исключения, вызванные промежуточным программным обеспечением, преобразуются в соответствующий ответ HTTP, а затем передаются следующему промежуточному программному обеспечению.В
MIDDLEWARE_CLASSES, если методprocess_responseвызывает исключение, методыprocess_responseвсех более ранних промежуточных программ пропускаются и всегда возвращается HTTP-ответ500 Internal Server Error(даже если возникшее исключение было, например,Http404). При настройкеMIDDLEWAREисключение, вызванное промежуточным ПО, будет немедленно преобразовано в соответствующий HTTP-ответ, а затем следующее промежуточное ПО увидит этот ответ. Промежуточное программное обеспечение никогда не пропускается из-за того, что промежуточное программное обеспечение вызывает исключение.
Support for asynchronous requests was added to the MiddlewareMixin.