Spec-Zone.ru › Django 3.1

Среднее ПО

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

Опять же, обработчики промежуточного программного обеспечения (middleware) выполняются в обратном порядке во время фазы ответа, что включает process_exception. Если обработчик исключений возвращает ответ, методы process_exception классов обработчиков middleware, расположенных выше этого обработчика, вообще не будут вызываться.

process_template_response()

process_template_response(request, response)

request — это объект HttpRequest. response — это объект TemplateResponse (или эквивалент), возвращённый представлением Django или обработчиком middleware.

process_template_response() вызывается сразу после завершения выполнения представления, если у экземпляра ответа есть метод render(), указывающий, что это — TemplateResponse или эквивалент.

Он должен вернуть объект ответа, реализующий метод render. Он может изменить заданный response путём изменения response.template_name и response.context_data, или он может создать и вернуть совершенно новый TemplateResponse или эквивалент.

Вам не нужно явно рендерить ответы — ответы будут автоматически рендериться, после того, как будут вызваны все обработчики middleware для ответов.

Обработчики middleware выполняются в обратном порядке во время фазы ответа, что включает process_template_response().

Обработка потоковых ответов

В отличие от HttpResponse, StreamingHttpResponse не имеет атрибута content. В результате обработчики middleware больше не могут полагаться на то, что все ответы будут иметь атрибут content. Если им нужен доступ к содержимому, они должны проверить, является ли ответ потоковым, и соответствующим образом скорректировать своё поведение:

if response.streaming:
    response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
    response.content = alter_content(response.content)

Примечание

Следует предполагать, что streaming_content слишком велик, чтобы храниться в памяти. Обработчики middleware могут обернуть его в новый генератор, но не должны его потреблять. Обертывание обычно реализуется следующим образом:

def wrap_streaming_content(content):
    for chunk in content:
        yield alter_content(chunk)

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

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

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

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

Новое в Django 3.1.

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

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

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

Если ваш обработчик middleware имеет и sync_capable = True, и async_capable = True, Django передаст ему запрос без преобразования. В этом случае вы можете определить, получит ли ваш обработчик middleware асинхронные запросы, проверив, является ли переданный вам объект get_response функцией-генератором, используя asyncio.iscoroutinefunction().

Модуль django.utils.decorators содержит декораторы sync_only_middleware(), async_only_middleware() и sync_and_async_middleware(), которые позволяют вам применять эти флаги к фабричным функциям обработчиков middleware.

Возвращаемый вызываемый объект должен соответствовать синхронной или асинхронной природе метода get_response. Если у вас есть асинхронный get_response, вы должны вернуть функцию-генератор (async def).

Методы process_view, process_template_response и process_exception, если они предоставляются, также должны быть адаптированы для соответствия синхронно/асинхронному режиму. Однако Django будет индивидуально адаптировать их по мере необходимости, если вы этого не сделаете, с дополнительной потерей производительности.

Вот пример того, как создать функцию обработчика middleware, которая поддерживает оба режима:

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

Примечание

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

Таким образом, даже если вы обёртываете асинхронное представление, вы можете быть вызваны в синхронном режиме, если между вами и представлением есть другие синхронные обработчики middleware.

Модернизация обработчиков middleware в стиле до Django 1.10

class django.utils.deprecation.MiddlewareMixin

Django предоставляет django.utils.deprecation.MiddlewareMixin для облегчения создания классов обработчиков middleware, совместимых как с MIDDLEWARE, так и со старым стилем MIDDLEWARE_CLASSES, и поддерживающих синхронные и асинхронные запросы. Все классы обработчиков middleware, входящие в состав Django, совместимы с обоими наборами настроек.

Mixin предоставляет метод __init__(), который требует аргумент get_response и сохраняет его в self.get_response.

Метод __call__():

  1. Вызывает self.process_request(request) (если определён).
  2. Вызывает self.get_response(request) для получения ответа от последующих обработчиков middleware и представления.
  3. Вызывает self.process_response(request, response) (если определён).
  4. Возвращает ответ.

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

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

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

  1. В MIDDLEWARE_CLASSES, каждый обработчик middleware всегда будет иметь вызываемый метод process_response, даже если предыдущий обработчик middleware сократил путь, вернув ответ из своего метода process_request. В MIDDLEWARE, обработчики middleware ведут себя более как слои лука: слои, через которые проходит ответ при выходе, — те же слои, которые видели запрос при входе. Если обработчик middleware сокращает путь, только этот обработчик middleware и обработчики middleware, предшествующие ему в MIDDLEWARE, увидят ответ.
  2. В MIDDLEWARE_CLASSES, process_exception применяется к исключениям, поднятым из метода обработчика middleware process_request. В MIDDLEWARE, process_exception применяется только к исключениям, поднятым из представления (или из метода render TemplateResponse). Исключения, поднятые обработчиком middleware, преобразуются в соответствующий HTTP-ответ и затем передаются следующему обработчику middleware.
  3. В MIDDLEWARE_CLASSES, если метод process_response поднимет исключение, методы process_response всех предыдущих обработчиков middleware пропускаются, и всегда возвращается HTTP-ответ 500 Internal Server Error (даже если поднятое исключение было, например, Http404). В MIDDLEWARE, исключение, поднятое обработчиком middleware, немедленно преобразуется в соответствующий HTTP-ответ, а затем следующий обработчик middleware в очереди увидит этот ответ. Обработчики middleware никогда не пропускаются из-за поднятия исключения обработчиком middleware.
Изменено в Django 3.1:

Поддержка асинхронных запросов была добавлена в MiddlewareMixin.

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

Spec-Zone.ru

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