Промежуточное ПО
Промежуточное ПО — это фреймворк хуков для обработки запросов/ответов в Django. Это лёгкая, низкоуровневая система «плагинов» для глобального изменения входных или выходных данных Django.
Каждый компонент промежуточного ПО отвечает за выполнение определённой функции. Например, Django включает компонент промежуточного ПО, AuthenticationMiddleware, который ассоциирует пользователей с запросами, используя сессии.
Этот документ объясняет, как работает промежуточное ПО, как его активировать и как написать собственное промежуточное ПО. Django поставляется с некоторым встроенным промежуточным ПО, которое можно использовать сразу. Они документированы в справочнике по встроенному промежуточному ПО.
Был представлен новый стиль промежуточного ПО для использования с новым MIDDLEWARE параметром. Если вы используете старый MIDDLEWARE_CLASSES параметр, вам нужно будет адаптировать старое пользовательское промежуточное ПО перед использованием нового параметра. В этом документе описано промежуточное ПО нового стиля. Обратитесь к этой странице в более старых версиях документации для описания работы промежуточного ПО старого стиля.
Создание собственного промежуточного ПО
Фабрика промежуточного ПО — это вызываемый объект, который принимает вызываемый объект 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(object):
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.
__init__(get_response)
Фабрики промежуточного ПО должны принимать аргумент get_response. Вы также можете инициализировать некоторое глобальное состояние для промежуточного ПО. Имейте в виду несколько замечаний:
- Django инициализирует ваше промежуточное ПО только с аргументом
get_response, поэтому вы не можете определить__init__()как требующий каких-либо других аргументов. - В отличие от метода
__call__(), который вызывается один раз на запрос,__init__()вызывается только один раз, при запуске веб-сервера.
В более старых версиях __init__() не вызывался до тех пор, пока веб-сервер не ответит на первый запрос.
В более старых версиях __init__() не принимал никаких аргументов. Для возможности использования вашего промежуточного ПО в Django 1.9 и более ранних версиях сделайте get_response необязательным аргументом (get_response=None).
Отметка промежуточного ПО как неиспользуемого
Иногда полезно определить во время запуска, следует ли использовать компонент промежуточного ПО. В таких случаях метод __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.
Обновление обработчиков по старому стилю (до 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_CLASSESprocess_exceptionприменяется к исключениям, поднятым из метода обработчикаprocess_request. При использованииMIDDLEWAREprocess_exceptionприменяется только к исключениям, поднятым из представления (или из методаrenderобъектаTemplateResponse). Исключение, поднятое обработчиком, преобразуется в соответствующий 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/1.11/topics/http/middleware/