Middleware
Middleware — это фреймворк хуков в обработке запросов/ответов Django. Это лёгкая, низкоуровневая система «плагинов» для глобального изменения ввода или вывода Django.
Каждый компонент middleware отвечает за выполнение определённой функции. Например, Django включает компонент middleware, AuthenticationMiddleware, который связывает пользователей с запросами, используя сессии.
Этот документ объясняет, как работает middleware, как активировать middleware и как написать собственный middleware. Django поставляется с некоторыми встроенными middleware, которые можно использовать сразу. Они документированы в справочнике по встроенным middleware.
Написание собственного middleware
Фабрика middleware — это вызываемый объект, который принимает вызываемый объект get_response и возвращает middleware. Middleware — это вызываемый объект, который принимает запрос и возвращает ответ, как и представление.
Middleware может быть написан как функция, которая выглядит так:
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, может быть фактическим представлением (если это последний указанный middleware) или следующим middleware в цепочке. Текущему middleware не нужно знать или заботиться о том, что именно это, просто что оно представляет то, что идёт дальше.
Это небольшое упрощение — вызываемый объект get_response для последнего middleware в цепочке не будет фактическим представлением, а методом-обёрткой из обработчика, который позаботится о применении middleware для представлений, вызове представления с соответствующими аргументами URL и применении middleware для ответов шаблонов и middleware для исключений.
Middleware может находиться где угодно на вашем пути Python.
__init__(get_response)
Фабрики middleware должны принимать аргумент get_response. Вы также можете инициализировать некоторое глобальное состояние для middleware. Имейте в виду несколько оговорок:
- Django инициализирует ваш middleware только с аргументом
get_response, поэтому вы не можете определить__init__()как требующий какие-либо другие аргументы. - В отличие от метода
__call__(), который вызывается один раз на запрос,__init__()вызывается только один раз, когда веб-сервер запускается.
Отмечание middleware как неиспользуемого
Иногда полезно определить во время запуска, следует ли использовать часть middleware. В таких случаях метод __init__() вашего middleware может вызвать MiddlewareNotUsed. Django затем удалит этот middleware из процесса middleware и запишет сообщение отладки в логгер django.request, когда DEBUG имеет значение True.
Активация middleware
Для активации компонента middleware добавьте его в список MIDDLEWARE в настройках Django.
В MIDDLEWARE, каждый компонент middleware представлен строкой: полным путём к классу или имени функции фабрики 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 — MIDDLEWARE может быть пустым, если хотите, — но настоятельно рекомендуется по крайней мере использовать CommonMiddleware.
Порядок в MIDDLEWARE имеет значение, потому что один middleware может зависеть от другого. Например, AuthenticationMiddleware сохраняет аутентифицированного пользователя в сессии; поэтому он должен выполняться после SessionMiddleware. См. Порядок middleware для некоторых общих советов по порядку классов middleware Django.
Порядок и слоирование middleware
На фазе запроса, перед вызовом представления, Django применяет middleware в порядке, определённом в MIDDLEWARE, сверху вниз.
Можно представить это как лук: каждый класс middleware — это «слой», который оборачивает представление, которое находится в ядре лука. Если запрос проходит через все слои лука (каждый из них вызывает get_response для передачи запроса в следующий слой), до представления в ядре, то ответ затем пройдёт через каждый слой (в обратном порядке) по пути наружу.
Если один из слоёв решает прервать выполнение и вернуть ответ, не вызвав свой get_response, ни один из слоёв лука внутри этого слоя (включая представление) не увидит запрос или ответ. Ответ вернётся только через те же слои, через которые прошёл запрос.
Другие хуки middleware
Помимо описанного ранее базового шаблона middleware запросов/ответов, вы можете добавить три других специальных метода в middleware на основе классов:
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 продолжит обработку этого запроса, выполняя любой другой middleware для обработки запроса и затем соответствующее представление. Если он возвращает объект HttpResponse, Django не станет беспокоиться о вызове соответствующего представления; он применит middleware для обработки ответа к этому объекту HttpResponse и вернёт результат.
Примечание
Доступ к request.POST внутри middleware до запуска представления или в process_view() предотвратит выполнение любого представления после middleware, способного изменять обработчики загрузок для запроса, и обычно следует избегать этого.
Класс CsrfViewMiddleware можно считать исключением, поскольку он предоставляет декораторы csrf_exempt() и csrf_protect(), которые позволяют представлениям явно контролировать, в какой момент должна произойти проверка CSRF.
process_exception()
-
process_exception(request, exception)
request — это объект HttpRequest. exception — это объект Exception, поднятый функцией представления.
Django вызывает process_exception(), когда представление поднимает исключение. process_exception() должно вернуть либо None, либо объект HttpResponse. Если он возвращает объект HttpResponse, будут применены middleware для обработки ответа шаблона и полученный ответ будет возвращён браузеру. В противном случае включается обработка исключений по умолчанию.
Опять же, middleware выполняется в обратном порядке во время фазы ответа, что включает process_exception. Если middleware для обработки исключений возвращает ответ, методы process_exception классов middleware выше этого middleware не будут вызваны вообще.
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.
Обновление промежуточного программного обеспечения в стиле 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() напрямую.
В большинстве случаев наследование от этого миксина будет достаточно для того, чтобы сделать промежуточное программное обеспечение старого стиля совместимым с новой системой с достаточной обратной совместимостью. Новые семантика короткого замыкания будут безвредными или даже полезными для существующего промежуточного программного обеспечения. В некоторых случаях классу промежуточного программного обеспечения могут потребоваться изменения для адаптации к новым семантикам.
Вот различия в поведении при использовании 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/2.1/topics/http/middleware/