Spec-Zone.ru › Flask 0.12

Контекст запроса

Этот документ описывает поведение Flask 0.7, которое в основном соответствует старому поведению, но имеет некоторые небольшие, тонкие различия.

Рекомендуется сначала прочитать главу Контекст приложения.

Погружение в локальные переменные контекста

Представьте, у вас есть вспомогательная функция, которая возвращает URL, на который должен быть перенаправлен пользователь. Предположим, она всегда перенаправляет на URL, параметр next которого, или HTTP referrer, или главную страницу:

from flask import request, url_for

def redirect_url():
    return request.args.get('next') or \
           request.referrer or \
           url_for('index')

Как вы видите, она обращается к объекту запроса. Если вы попытаетесь запустить это из обычной оболочки Python, вы увидите следующую ошибку:

>>> redirect_url()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'request'

Это имеет смысл, потому что в настоящее время у нас нет запроса, к которому мы могли бы получить доступ. Поэтому нам нужно создать запрос и привязать его к текущему контексту. Метод test_request_context может создать для нас RequestContext:

>>> ctx = app.test_request_context('/?next=http://example.com/')

Этот контекст можно использовать двумя способами. Либо с помощью оператора with , либо вызвав методы push() и pop():

>>> ctx.push()

С этого момента вы можете работать с объектом запроса:

>>> redirect_url()
u'http://example.com/'

Пока вы не вызовете pop:

>>> ctx.pop()

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

Для получения дополнительной информации о том, как использовать контекст запроса из интерактивной оболочки Python, перейдите к главе Работа с оболочкой.

Как работает контекст

Если вы посмотрите на то, как внутренне работает приложение Flask WSGI, вы найдете код, который выглядит примерно так:

def wsgi_app(self, environ):
    with self.request_context(environ):
        try:
            response = self.full_dispatch_request()
        except Exception as e:
            response = self.make_response(self.handle_exception(e))
        return response(environ, start_response)

Метод request_context() возвращает новый RequestContext объект и использует его в сочетании с оператором with для привязки контекста. Всё, что вызывается из одной и той же нити с этого момента до конца оператора with , будет иметь доступ к глобальным переменным запроса (flask.request и другие).

Контекст запроса внутренне работает как стек: верхний уровень стека — это текущий активный запрос. push() добавляет контекст в самый верх стека, pop() удаляет его из стека. При извлечении также вызываются функции приложения teardown_request().

Ещё один момент: контекст запроса автоматически также создаст контекст приложения контекст приложения, когда он будет помещён в стек, если для данного приложения контекст приложения ещё не существует.

Обработчики и ошибки

Что произойдёт, если во время обработки запроса в Flask возникнет ошибка? Это поведение изменилось в версии 0.7, потому что мы хотели сделать его более понятным. Новое поведение довольно просто:

  1. Перед каждым запросом вызываются функции before_request(). Если одна из этих функций возвращает ответ, другие функции больше не вызываются. Однако значение возврата в любом случае рассматривается как замена значения возврата представления.
  2. Если функции before_request() не вернули ответ, запускается обычная обработка запроса, и функция представления, которая была выбрана, получает возможность вернуть ответ.
  3. Значение возврата представления преобразуется в объект ответа и передаётся функциям after_request(), которые могут заменить его или изменить его на месте.
  4. В конце запроса вызываются функции teardown_request(). Это всегда происходит, даже если в дальнейшем произошла необработанная ошибка, или обработчик до запроса не был вызван вообще (например, в тестовых средах иногда вы можете захотеть не выполнять обработчики до запроса).

А что происходит с ошибками? В режиме производства, если исключение не перехвачено, вызывается обработчик внутренней ошибки 500. В режиме разработки исключение не обрабатывается дальше и передаётся серверу WSGI. Таким образом, такие инструменты, как интерактивный отладчик, могут предоставить полезную отладочную информацию.

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

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

Обработчики завершения

Обработчики завершения — это специальные обработчики, которые выполняются в разное время. Строго говоря, они независимы от фактической обработки запроса, так как они привязаны к жизненному циклу объекта RequestContext. При извлечении контекста запроса вызываются функции teardown_request().

Это важно знать, если продолжительность жизни контекста запроса продлевается с помощью тестового клиента в операторе with или при использовании контекста запроса из командной строки:

with app.test_client() as client:
    resp = client.get('/foo')
    # the teardown functions are still not called at that point
    # even though the response ended and you have the response
    # object in your hand

# only when the code reaches this point the teardown functions
# are called.  Alternatively the same thing happens if another
# request was triggered from the test client

Поведение легко проверить из командной строки:

>>> app = Flask(__name__)
>>> @app.teardown_request
... def teardown_request(exception=None):
...     print 'this runs after request'
...
>>> ctx = app.test_request_context()
>>> ctx.push()
>>> ctx.pop()
this runs after request
>>>

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

Примечания по прокси

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

Большинство времени вы можете об этом не беспокоиться, но есть исключения, когда полезно знать, что этот объект является прокси:

  • Объекты-прокси не имитируют их наследуемые типы, поэтому, если вы хотите выполнить проверки экземпляров, вы должны делать это на экземпляре, который проксируется (см. _get_current_object ниже).
  • Если ссылка на объект важна (например, для отправки Сигналов)

Если вам нужен доступ к базовому объекту, который проксируется, вы можете использовать метод _get_current_object():

app = current_app._get_current_object()
my_signal.send(app)

Сохранение контекста при ошибке

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

Начиная с Flask 0.7, вы можете более тонко контролировать это поведение, задавая переменную конфигурации PRESERVE_CONTEXT_ON_EXCEPTION. По умолчанию она связана с настройкой DEBUG. Если приложение находится в режиме отладки, контекст сохраняется; в режиме производства — нет.

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

© 2007–2020 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/0.12.x/reqcontext/

Spec-Zone.ru

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