Контекст запроса
Контекст запроса отслеживает данные на уровне запроса во время обработки запроса. Вместо передачи объекта запроса каждой функции, выполняемой во время запроса, вместо этого используются прокси 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–2022 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/2.1.x/reqcontext/