Spec-Zone.ru › Django 6.0

Промежуточное ПО

Промежуточное ПО — это система хуков для обработки запросов и ответов в 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 для последнего компонента промежуточного ПО в цепочке будет не фактическое представление, а метод-обёртка обработчика, который отвечает за применение промежуточного ПО представления, вызов представления с соответствующими аргументами URL и применение промежуточного ПО ответа шаблона и промежуточного ПО исключений.

Промежуточное ПО может поддерживать только синхронный Python (по умолчанию), только асинхронный Python или оба режима. Подробные сведения о том, как объявить поддерживаемые режимы и определить тип получаемого запроса, см. в разделе Поддержка асинхронного режима.

Промежуточное ПО может находиться в любом месте в пути Python.

__init__(get_response)

Фабрики промежуточного ПО должны принимать аргумент get_response. Также можно инициализировать некоторое глобальное состояние промежуточного ПО. Учтите несколько нюансов:

  • Django инициализирует промежуточное ПО, передавая только аргумент get_response, поэтому нельзя определять __init__() так, чтобы он требовал каких-либо других аргументов.
  • В отличие от метода __call__(), который вызывается при каждом запросе, метод __init__() вызывается только один раз при запуске веб-сервера.

Пометка промежуточного ПО как неиспользуемого

Иногда полезно определить во время запуска, следует ли использовать компонент промежуточного ПО. В таких случаях метод __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)

StreamingHttpResponse поддерживает как синхронные, так и асинхронные итераторы. Функция-обёртка должна соответствовать их типу. Проверьте StreamingHttpResponse.is_async, если промежуточному ПО нужно поддерживать оба типа итераторов.

Обработка исключений

Django автоматически преобразует исключения, вызванные представлением или промежуточным ПО, в соответствующий HTTP-ответ с кодом ошибки. Некоторые исключения преобразуются в коды состояния 4xx, а неизвестное исключение — в код состояния 500.

Это преобразование выполняется до и после каждого компонента промежуточного ПО (его можно представить как тонкую плёнку между слоями луковицы), поэтому каждый компонент всегда может рассчитывать на получение какого-либо HTTP-ответа при вызове своего вызываемого объекта get_response. Промежуточному ПО не нужно оборачивать вызов get_response в try/except и обрабатывать исключение, которое мог вызвать более поздний компонент промежуточного ПО или представление. Например, даже если следующий компонент в цепочке вызовет исключение Http404, ваше промежуточное ПО не увидит это исключение; вместо этого оно получит объект HttpResponse со значением 404 в атрибуте status_code.

Чтобы пропустить это преобразование и передавать исключения выше по цепочке, установите для DEBUG_PROPAGATE_EXCEPTIONS значение True.

Поддержка асинхронного режима

Промежуточное ПО может поддерживать любые сочетания синхронных и асинхронных запросов. Если оно не поддерживает оба режима, Django адаптирует запросы к его требованиям, но это снизит производительность.

По умолчанию Django предполагает, что промежуточное ПО способно обрабатывать только синхронные запросы. Чтобы изменить это предположение, задайте следующие атрибуты для функции-фабрики или класса промежуточного ПО:

  • sync_capable — логическое значение, указывающее, может ли промежуточное ПО обрабатывать синхронные запросы. По умолчанию — True.
  • async_capable — логическое значение, указывающее, может ли промежуточное ПО обрабатывать асинхронные запросы. По умолчанию — False.

Если у промежуточного ПО заданы оба атрибута — sync_capable = True и async_capable = True, — Django передаст ему запрос без преобразования. В этом случае определить, получит ли промежуточное ПО асинхронный запрос, можно, проверив, является ли переданный объект get_response функцией-корутиной, с помощью asgiref.sync.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 адаптирует каждый из них отдельно по мере необходимости, что дополнительно снизит производительность.

Ниже показано, как создать функцию промежуточного ПО, поддерживающую оба режима:

from asgiref.sync import iscoroutinefunction
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 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 оптимизирует стек вызовов промежуточного ПО, чтобы свести к минимуму число переходов между синхронным и асинхронным режимами.

Поэтому даже при обёртывании асинхронного представления вызов может выполняться в синхронном режиме, если между вами и представлением находится другое синхронное промежуточное ПО.

При использовании асинхронного промежуточного ПО на основе классов необходимо убедиться, что его экземпляры правильно помечены как функции-корутины:

from asgiref.sync import iscoroutinefunction, markcoroutinefunction


class AsyncMiddleware:
    async_capable = True
    sync_capable = False

    def __init__(self, get_response):
        self.get_response = get_response
        if iscoroutinefunction(self.get_response):
            markcoroutinefunction(self)

    async def __call__(self, request):
        response = await self.get_response(request)
        # Some logic ...
        return response

Обновление промежуточного ПО в стиле до 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(request, 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-ответ, который затем получает следующий компонент в цепочке. Если промежуточное ПО вызывает исключение, другие компоненты промежуточного ПО никогда не пропускаются.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/topics/http/middleware/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API