Контекст запроса
Контекст запроса отслеживает данные уровня запроса во время обработки запроса. Вместо передачи объекта запроса каждой функции, выполняемой во время запроса, используются прокси request и session.
Это аналогично Контексту приложения, который отслеживает данные уровня приложения независимо от запроса. Соответствующий контекст приложения также помещается на стек, когда помещается контекст запроса.
Назначение контекста
При обработке запроса приложением Flask создается объект Request на основе окружения, полученного от сервера WSGI. Поскольку один воркер (поток, процесс или корутина в зависимости от сервера) обрабатывает только один запрос за раз, данные запроса можно рассматривать как глобальные для этого воркера во время этого запроса. Flask использует термин контекстно-локальные для этого.
Flask автоматически помещает контекст запроса при обработке запроса. Функции представления, обработчики ошибок и другие функции, выполняемые во время запроса, получат доступ к прокси request, который указывает на объект запроса для текущего запроса.
Жизненный цикл контекста
Когда приложение Flask начинает обрабатывать запрос, оно помещает контекст запроса, который также помещает Контекст приложения. Когда запрос завершается, контекст запроса удаляется, а затем удаляется контекст приложения.
Контекст уникален для каждого потока (или другого типа воркера). request нельзя передать в другой поток, так как у другого потока будет другой стек контекстов, и он не будет знать о запросе, на который ссылался родительский поток.
Контекстно-локальные переменные реализованы в Werkzeug. Подробнее о том, как это работает внутри, см. в разделе Контекстно-локальные переменные.
Вручную поместить контекст
Если вы попытаетесь получить доступ к 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', data={'format': 'short'}):
generate_report()
Если вы видите эту ошибку где-то еще в своём коде, не связанном с тестированием, это, скорее всего, означает, что вам нужно перенести этот код в функцию представления.
Сведения о том, как использовать контекст запроса из интерактивной оболочки Python, см. в разделе Работа с оболочкой.
Как работает контекст
Метод Flask.wsgi_app() используется для обработки каждого запроса. Он управляет контекстами во время запроса. Внутри контексты запроса и приложения работают как стеки, _request_ctx_stack и _app_ctx_stack. Когда контексты помещаются в стек, прокси, зависящие от них, становятся доступными и указывают на информацию из верхнего контекста в стеке.
Когда начинается запрос, создается и помещается на стек RequestContext, который сначала создаёт и помещает на стек AppContext, если контекст для этого приложения ещё не находится в верхней части стека. Пока эти контексты находятся в стеке, прокси current_app, g, request и session доступны для исходного потока, обрабатывающего запрос.
Поскольку контексты находятся в стеке, другие контексты могут быть помещены в стек, чтобы изменить прокси во время запроса. Хотя это не распространённый паттерн, он может использоваться в сложных приложениях для, например, внутренних редиректов или объединения разных приложений.
После обработки запроса и создания и отправки ответа контекст запроса удаляется, что затем удаляет контекст приложения. Непосредственно перед удалением вызываются функции teardown_request() и teardown_appcontext(). Эти функции вызываются даже если во время обработки возникло необработанное исключение.
Обработчики обращений и ошибки
Flask обрабатывает запрос в несколько этапов, что может повлиять на запрос, ответ и то, как обрабатываются ошибки. Контексты активны на всех этих этапах.
Blueprint может добавлять обработчики для этих событий, специфичных для модуля. Обработчики модуля будут работать, если модуль владеет маршрутом, соответствующим запросу.
- Перед каждым запросом вызываются функции
before_request(). Если одна из этих функций возвращает значение, остальные функции пропускаются. Возвращаемое значение обрабатывается как ответ, и функция представления не вызывается. - Если функции
before_request()не вернули ответ, вызывается функция представления для соответствующего маршрута и возвращается ответ. - Возвращаемое значение представления преобразуется в фактический объект ответа и передается функциям
after_request(). Каждая функция возвращает изменённый или новый объект ответа. - После возвращения ответа контексты удаляются, что вызывает функции
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
Сигналы
Если signals_available равно true, отправляются следующие сигналы:
-
request_startedотправляется перед вызовом функцийbefore_request(). -
request_finishedотправляется после вызова функцийafter_request(). -
got_request_exceptionотправляется, когда начинает обрабатываться исключение, но перед тем, как будет найдена или вызвана функцияerrorhandler(). -
request_tearing_downотправляется после вызова функцийteardown_request().
Сохранение контекста при ошибке
В конце запроса контекст запроса удаляется, и все данные, связанные с ним, уничтожаются. Если во время разработки произойдёт ошибка, для целей отладки полезно отложить уничтожение данных.
Когда сервер разработки работает в режиме разработки (переменная среды FLASK_ENV установлена в значение 'development'), ошибка и данные сохранятся и будут показаны в интерактивном отладчике.
Это поведение можно контролировать с помощью конфигурации PRESERVE_CONTEXT_ON_EXCEPTION. Как описано выше, по умолчанию в среде разработки оно равно True.
Не включайте PRESERVE_CONTEXT_ON_EXCEPTION в рабочей среде, так как это приведёт к утечке памяти вашего приложения при возникновении исключений.
Примечания по прокси
Некоторые объекты, предоставляемые Flask, являются прокси для других объектов. К прокси обращаются одинаково для каждого потока-рабочего, но они указывают на уникальный объект, связанный с каждым рабочим потоком, как описано на этой странице.
Большинство времени вы можете этого не замечать, но есть исключения, когда важно знать, что этот объект является прокси:
- Объекты-прокси не могут подделывать свой тип как фактические типы объектов. Если вы хотите выполнить проверки экземпляра, вы должны сделать это на объекте, который проксируется.
- Ссылка на проксируемый объект необходима в некоторых ситуациях, таких как отправка сигналов или передача данных в фоновый поток.
Если вам нужно получить доступ к базовому объекту, который проксируется, используйте метод _get_current_object():
app = current_app._get_current_object() my_signal.send(app)
© 2007–2021 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/2.0.x/reqcontext/