Spec-Zone.ru › Django 4.2

Среднее ПО

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

Опять же, среднее ПО запускается в обратном порядке во время фазы ответа, что включает 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 4.2:

Добавлена поддержка потоковых ответов с асинхронными итераторами.

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

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

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

Вы можете установить 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, совместимы с обоими вариантами настроек.

Mixin предоставляет метод __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() напрямую.

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

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

  1. Под MIDDLEWARE_CLASSES, каждый middleware всегда вызовет свой метод process_response, даже если предыдущий middleware прервал выполнение, вернув ответ из своего метода process_request. Согласно 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 никогда не пропускаются из-за возникшего в них исключения.

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

Spec-Zone.ru

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