Контекст запроса
Контекст запроса отслеживает данные на уровне запроса во время обработки запроса. Вместо передачи объекта запроса каждой функции, выполняемой во время запроса, вместо этого используются прокси 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 может добавлять обработчики этих событий, специфичные для этого Blueprint. Обработчики Blueprint выполняются, если Blueprint владеет маршрутом, который соответствует запросу.
- Перед каждым запросом вызываются функции
before_request(). Если одна из этих функций возвращает значение, остальные функции пропускаются. Возвращаемое значение обрабатывается как ответ, и функция представления не вызывается. - Если функции
before_request()не вернули ответ, вызывается функция представления для соответствующего маршрута и возвращается ответ. - Возвращаемое значение представления преобразуется в фактический объект ответа и передаётся функциям
after_request(). Каждая функция возвращает изменённый или новый объект ответа. - После возвращения ответа контексты извлекаются, вызывая функции
teardown_request()иteardown_appcontext(). Эти функции вызываются даже если в любой момент выше была поднята необработанная ошибка.
Если ошибка возникает до функций разбора, Flask пытается сопоставить её с функцией errorhandler() для обработки ошибки и возврата ответа. Если обработчик ошибок не найден или сам обработчик вызывает ошибку, Flask возвращает универсальный 500 Internal Server Error ответ. Функции разбора всё ещё вызываются и получают объект исключения.
Если режим отладки включён, необработанные исключения не преобразуются в ответ 500 и вместо этого передаются серверу WSGI. Это позволяет серверу разработки отобразить отладчик с traceback.
Обработчики завершения
Обработчики завершения независимы от обработки запроса и вместо этого вызываются контекстами при их извлечении. Функции вызываются даже если во время обработки запроса возникла необработанная ошибка, а также для вручную помещённых контекстов. Это означает, что нет гарантии, что другие части обработки запроса будут выполнены раньше. Убедитесь, что эти функции написаны так, чтобы не зависели от других обработчиков и не завершались с ошибкой.
Во время тестирования может быть полезно отложить извлечение контекстов после завершения запроса, чтобы их данные можно было получить в тестовой функции. Используйте 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
Сигналы
Отправляются следующие сигналы:
-
request_startedотправляется до вызова функцийbefore_request(). -
request_finishedотправляется после вызова функцийafter_request(). -
got_request_exceptionотправляется, когда обработка исключения начинается, но до того, как ищется или вызываетсяerrorhandler(). -
request_tearing_downотправляется после вызова функцийteardown_request().
Заметки о прокси
Некоторые объекты, предоставляемые Flask, являются прокси для других объектов. К прокси обращаются одинаково для каждой рабочей нити, но они указывают на уникальный объект, привязанный к каждой рабочей нити за кулисами, как описано на этой странице.
В большинстве случаев вам не нужно об этом заботиться, но есть некоторые исключения, когда важно знать, что этот объект на самом деле является прокси:
- Объекты-прокси не могут подделать свой тип как фактические типы объектов. Если вы хотите выполнить проверки экземпляра, вы должны сделать это на объекте, который проксируется.
- Ссылка на проксируемый объект необходима в некоторых ситуациях, таких как отправка сигналов или передача данных в фоновую нить.
Если вам нужно получить доступ к базовому объекту, который проксируется, используйте метод %%%CODE_BLOCK_45%%:
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.3.x/reqcontext/