Контекст запроса веб-приложения на Flask отслеживает данные уровня запроса во время выполнения запроса. Вместо передачи каждой функции объекта запроса, которая выполняется во время запроса, Flask осуществляет доступ к прокси-объекту запроса flask.request
и сеанса flask.session
.
Такое поведение похоже на контекст приложения, который отслеживает данные уровня приложения независимо от запроса. Соответствующий контекст приложения передается при передаче контекста запроса.
Когда приложение Flask обрабатывает запрос, то оно создает объект Request
на основе среды, полученной от сервера WSGI. Поскольку рабочий процесс (поток, процесс или сопрограмма в зависимости от сервера) обрабатывает только один запрос за раз, данные запроса могут считаться глобальными для этого воркера во время этого запроса. Flask использует для этого термин "локальный контекст".
Flask автоматически проталкивает контекст запроса при обработке запроса. Функции просмотра, обработчики ошибок и другие функции, которые выполняются во время запроса, будут иметь доступ к прокси-объекту запроса flask.request
, который указывает на объект запроса flask.Request
для текущего запроса.
Когда приложение Flask начинает обрабатывать запрос, то оно проталкивает контекст запроса, который также проталкивает контекст приложения. Когда запрос заканчивается, появляется контекст запроса, а затем контекст приложения.
Контекст уникален для каждого потока или другого типа воркера. Запрос flask.request
не может быть передан другому потоку, другой поток будет иметь другой стек контекста и не будет знать о запросе, на который указывал родительский поток.
Локальные значения контекста реализованы с использованием Python contextvars
и Werkzeug LocalProxy
. Python автоматически управляет временем жизни контекстных переменных, а локальный прокси-сервер оборачивает этот низкоуровневый интерфейс, чтобы упростить работу с данными.
Если попытаться получить доступ к запросу или чему-либо, что его использует, вне контекста запроса, то появится следующее сообщение об ошибке:
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.
Обычно это должно происходить только при тестировании кода, который ожидает активного запроса. Один из вариантов - это использовать тестовый клиент для имитации полного запроса. Или можно использовать app.test_request_context()
в блоке with
, и все, что выполняется в блоке, будет иметь доступ к запросу flask.request
, заполненному тестовыми данными.
def generate_report(year): format = request.args.get('format') ... with app.test_request_context( '/make_report/2017', data={'format': 'short'}): generate_report()
Если видите эту ошибку где-то еще в своем коде, не связанном с тестированием, то это, скорее всего, указывает на то, что этот код следует переместить в функцию-представление (views).
Для обработки каждого запроса вызывается метод Flask.wsgi_app()
. Этот метод управляет контекстами во время запроса. Внутри, контексты запроса и приложения работают как стеки. При отправке контекстов становятся доступными прокси, которые зависят от них и указывают на информацию из верхнего элемента.
При запуске запроса создается и отправляется RequestContext
, если контекст для этого приложения еще не является верхним контекстом, то создается и отправляется AppContext
. Пока эти контексты передаются, прокси-объекты flask.current_app
, flask.g
, flask.request
и flask.session
доступны для исходного потока, обрабатывающего запрос.
Поскольку контексты представляют собой стеки, другие контексты могут быть отправлены для изменения прокси во время запроса. Хотя это не распространенный шаблон, но его можно использовать в расширенных приложениях на основе blueprint
, например, для выполнения внутренних перенаправлений или объединения различных приложений в цепочку.
После отправки запроса, генерации и отправки ответа приложения клиенту, контекст запроса, а затем и контекст приложения выталкиваются. Непосредственно перед их извлечением выполняются методы app.teardown_request()
и app.teardown_appcontext()
. Эти методы выполняются, даже если во время отправки ответа клиенту возникло необработанное исключение (т. е. в коде функции представлении возникла ошибка). Эти два метода полезны для освобождения открытых ресурсов во время подготовки запроса.
Flask отправляет запрос в несколько этапов, что может повлиять на запрос, ответ и способ обработки ошибок. Контексты активны на всех этих этапах.
Blueprint может добавлять обработчики для этих событий, специфичных для этой схемы. Обработчики схемы blueprint
будут запущены, если схеме принадлежит маршрут, соответствующий запросу.
@app.before_request()
. Если одна из таких функций возвращает значение, то другие функции пропускаются. Возвращаемое значение рассматривается как ответ сервера клиенту, а функция-представление не вызывается.@app.before_request()
не вернули ответ, то вызывается функция-представление для согласованного маршрута и возвращает ответ клиенту.@app.after_request()
. Каждая такая функция возвращает измененный или новый объект ответа.app.teardown_request()
и app.teardown_appcontext()
. Эти функции вызываются, даже если было вызвано необработанное исключение в любой точке выше.Если исключение возникает перед методами очистки .teardown*
, то Flask пытается сопоставить это исключение с методом app.errorhandler()
, чтобы обработать исключение и вернуть ответ клиенту. Если обработчик ошибок не найден или сам обработчик вызывает исключение, то Flask возвращает общий ответ 500 Internal Server Error
. При этом методы очистки app.teardown*
по-прежнему вызываются, и им передается объект исключения.
Если включен режим отладки, то необработанные исключения не преобразуются в ответ с кодом 500, а вместо этого передаются на сервер WSGI. Это позволяет серверу разработки представить интерактивный отладчик с трассировкой.
Обратные вызовы app.teardown*
не зависят от отправки запроса и вызываются контекстами, когда они выталкиваются. Функции вызываются, даже если во время подготовки или отправки ответа клиенту возникает необработанное исключение, а даже для контекстов, помещенных вручную. Это означает, что нет никакой гарантии, что какие-либо другие части отправки запроса будут выполнены раньше. Обязательно пишите эти функции таким образом, чтобы они не зависели от других обратных вызовов и не терпели неудачу.
Во время тестирования может быть полезно отложить извлечение контекстов после завершения запроса, чтобы их данные могли быть доступны в тестовой функции. Используйте app.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 вызываются # после контекста с выходами из блока with app.test_client() as client: client.get('/') # Контексты не выталкиваются, даже если запрос завершился print(request.path) # контексты выталкиваются и функции teardown # вызываются после выхода клиента с блоком
Отправляются следующие сигналы:
app.request_started
отправляется перед вызовом методов app.before_request()
.app.request_finished
отправляется после вызова методов app.after_request()
.app.got_request_exception
отправляется, когда начинает обрабатываться исключение, но до того, как будет найден или вызван app.errorhandler()
.app.request_tearing_down
отправляется после вызова методов app.teardown_request()
.Более подробно о сигналах Flask смотрите материал "Отладочные сигналы приложения Flask в Python".