Spec-Zone.ru › Flask

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

Контекст запроса отслеживает данные на уровне запроса во время обработки запроса. Вместо того, чтобы передавать объект запроса каждой функции, выполняющейся во время запроса, вместо этого обращаются к прокси request и session.

Это аналогично Контексту приложения, который отслеживает данные на уровне приложения независимо от запроса. Соответствующий контекст приложения добавляется при добавлении контекста запроса.

Назначение контекста

Когда приложение Flask обрабатывает запрос, оно создает объект Request на основе среды, полученной от WSGI-сервера. Поскольку рабочий элемент (поток, процесс или корутина в зависимости от сервера) обрабатывает только один запрос за раз, данные запроса можно считать глобальными для этого рабочего элемента во время этого запроса. Flask использует термин локальный контекст для этого.

Flask автоматически добавляет контекст запроса при обработке запроса. Функции представлений, обработчики ошибок и другие функции, выполняющиеся во время запроса, будут иметь доступ к прокси request, который указывает на объект запроса для текущего запроса.

Срок действия контекста

Когда приложение Flask начинает обрабатывать запрос, оно добавляет контекст запроса, который также добавляет контекст приложения. Когда запрос завершается, он удаляет контекст запроса, а затем и контекст приложения.

Контекст уникален для каждого потока (или другого типа рабочего элемента). request нельзя передать другому потоку; у другого потока есть отдельное пространство контекста, и он не будет знать о запросе, на который ссылался родительский поток.

Локальные переменные контекста реализованы с использованием contextvars Python и LocalProxy Werkzeug. Python автоматически управляет жизненным циклом переменных контекста, а прокси-объект локальной переменной оборачивает этот интерфейс низкого уровня, чтобы сделать работу с данными проще.

Добавление контекста вручную

Если вы попытаетесь получить доступ к request или чему-либо, что использует его, вне контекста запроса, получите сообщение об ошибке:

RuntimeError: Working outside of request context.

This typically means that you attempted to use functionality that
needed an active HTTP request. Consult the documentation on testing
for information about how to avoid this problem.

Это обычно происходит только при тестировании кода, ожидающего активный запрос. Один из вариантов — использовать test client для моделирования полного запроса. Либо можно использовать test_request_context() в with блоке, и все, что выполняется в блоке, будет иметь доступ к request, заполненному вашими тестовыми данными.

def generate_report(year):
    format = request.args.get("format")
    ...

with app.test_request_context(
    "/make_report/2017", query_string={"format": "short"}
):
    generate_report()

Если вы видите эту ошибку где-то ещё в своём коде, не связанном с тестированием, скорее всего, это означает, что вам следует перенести этот код в функцию представления.

Сведения о том, как использовать контекст запроса из интерактивной оболочки Python, см. в работе с оболочкой.

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

Метод Flask.wsgi_app() вызывается для обработки каждого запроса. Он управляет контекстами во время запроса. Внутренне контексты запроса и приложения работают как стеки. Когда контексты добавляются, прокси, зависящие от них, доступны и указывают на информацию из верхнего элемента.

Когда начинается запрос, создается и добавляется RequestContext, который сначала создает и добавляет AppContext, если контекст для этого приложения ещё не является верхним контекстом. Пока эти контексты добавлены, прокси current_app, g, request и session доступны исходному потоку, обрабатывающему запрос.

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

После обработки запроса и создания и отправки ответа контекст запроса удаляется, после чего удаляется контекст приложения. Непосредственно перед удалением вызываются функции teardown_request() и teardown_appcontext(). Они выполняются даже в случае возникновения необработанной ошибки во время обработки.

Обработчики событий и ошибки

Flask обрабатывает запрос поэтапно, что может повлиять на запрос, ответ и обработку ошибок. Контексты активны на всех этих этапах.

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

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

Если исключение возникает до вызова функций завершения, Flask пытается сопоставить его с функцией errorhandler() для обработки исключения и возврата ответа. Если обработчик ошибок не найден или сам обработчик вызывает исключение, Flask возвращает общий 500 Internal Server Error ответ. Функции завершения всё равно вызываются и получают объект исключения.

Если режим отладки включён, необработанные исключения не преобразуются в 500 ответ, а вместо этого передаются WSGI-серверу. Это позволяет серверу разработки отображать отладочную информацию с трассировкой.

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

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

Во время тестирования может быть полезно отложить удаление контекстов после завершения запроса, чтобы их данные можно было использовать в тестовой функции. Используйте test_client() в with блоке, чтобы сохранить контексты до выхода из with блока.

from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def hello():
    print('during view')
    return 'Hello, World!'

@app.teardown_request
def show_teardown(exception):
    print('after with block')

with app.test_request_context():
    print('during with block')

# teardown functions are called after the context with block exits

with app.test_client() as client:
    client.get('/')
    # the contexts are not popped even though the request ended
    print(request.path)

# the contexts are popped and teardown functions are called after
# the client with block exits

Сигналы

Отправляются следующие сигналы:

  1. request_started отправляется перед вызовом функций before_request().
  2. request_finished отправляется после вызова функций after_request().
  3. got_request_exception отправляется, когда обработка исключения начинается, но перед поиском или вызовом errorhandler().
  4. request_tearing_down отправляется после вызова функций teardown_request().

Заметки о прокси

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

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

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

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

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

© 2010 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/stable/reqcontext/

Spec-Zone.ru

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