Spec-Zone.ru › Django 1.10

Среднее программное обеспечение

Среднее программное обеспечение — это фреймворк хуков в обработке запросов/ответов Django. Это лёгкая, низкоуровневая система «плагинов» для глобального изменения входных или выходных данных Django.

Каждый компонент среднего программного обеспечения отвечает за выполнение определённой функции. Например, Django включает компонент среднего программного обеспечения, AuthenticationMiddleware, который связывает пользователей с запросами с помощью сессий.

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

Изменено в Django 1.10:

Был представлен новый стиль среднего программного обеспечения для использования с новым 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__() вызывается только один раз, когда веб-сервер запускается.
Изменено в Django 1.10:

В более ранних версиях __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__():

  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/1.10/topics/http/middleware/

Spec-Zone.ru

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