• 3.2
  • 5.0
  • 6.1
  • Версия документации: 3.1

Промежуточный слой (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. exceptionException, вызванное функцией представления.

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.

Асинхронная поддержка

New in Django 3.1.

Промежуточное программное обеспечение может поддерживать любую комбинацию синхронных и асинхронных запросов. 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__():

  1. Вызывает self.process_request(request) (если определено).

  2. Вызывает self.get_response(request) для получения ответа от более позднего промежуточного программного обеспечения и представления.

  3. Вызывает self.process_response(запрос, ответ) (если определено).

  4. Возвращает ответ.

При использовании с MIDDLEWARE_CLASSES метод __call__() никогда не будет использоваться; Django напрямую вызывает process_request() и process_response().

В большинстве случаев наследования от этого миксина будет достаточно, чтобы сделать промежуточное программное обеспечение старого стиля совместимым с новой системой с достаточной обратной совместимостью. Новая семантика короткого замыкания будет безвредна или даже полезна для существующего промежуточного программного обеспечения. В некоторых случаях классу промежуточного программного обеспечения могут потребоваться некоторые изменения, чтобы приспособиться к новой семантике.

Вот поведенческие различия между использованием MIDDLEWARE и MIDDLEWARE_CLASSES:

  1. В MIDDLEWARE_CLASSES каждое промежуточное программное обеспечение всегда будет вызывать свой методprocess_response, даже если более раннее промежуточное программное обеспечение закоротило, вернув ответ из своего методаprocess_request. В MIDDLEWARE промежуточное программное обеспечение ведет себя скорее как луковица: уровни, через которые проходит ответ на выходе, — это те же уровни, которые видели запрос на входе. Если промежуточное программное обеспечение закоротит, только это промежуточное программное обеспечение и те, которые находятся перед ним в MIDDLEWARE, увидят ответ.

  2. В MIDDLEWARE_CLASSES process_Exception применяется к исключениям, возникшим из метода промежуточного программного обеспечения process_request. В MIDDLEWARE process_Exception применяется только к исключениям, возникшим из представления (или из метода render TemplateResponse). Исключения, вызванные промежуточным программным обеспечением, преобразуются в соответствующий ответ HTTP, а затем передаются следующему промежуточному программному обеспечению.

  3. В MIDDLEWARE_CLASSES, если метод process_response вызывает исключение, методы process_response всех более ранних промежуточных программ пропускаются и всегда возвращается HTTP-ответ 500 Internal Server Error (даже если возникшее исключение было, например, Http404). При настройке MIDDLEWARE исключение, вызванное промежуточным ПО, будет немедленно преобразовано в соответствующий HTTP-ответ, а затем следующее промежуточное ПО увидит этот ответ. Промежуточное программное обеспечение никогда не пропускается из-за того, что промежуточное программное обеспечение вызывает исключение.

Changed in Django 3.1:

Support for asynchronous requests was added to the MiddlewareMixin.

Back to Top