Сообщить об ошибке.

Контекст запроса веб-приложения на Flask в Python.

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

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

Содержание:

Цель контекста запроса приложения Flask.

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

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

Время жизни контекста запроса приложения Flask.

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

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

Локальные переменные контекста реализованы в библиотеке Werkzeug.

Проталкивание контекста запроса вручную.

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

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(). Этот метод управляет контекстами во время запроса. Внутри, контексты запроса и приложения работают как стеки, flask._request_ctx_stack и flask._app_ctx_stack. Когда контексты помещаются в стек, то становятся доступными прокси-объекты, которые от них зависят, и указывают на информацию из верхнего контекста стека.

При запуске запроса создается и отправляется 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 
# вызываются после выхода клиента с блоком

Сохранение контекста запроса при ошибке.

В конце запроса контекст запроса выталкивается, и все данные, связанные с ним, уничтожаются. Если во время разработки возникает ошибка, то полезно отложить уничтожение данных для целей отладки.

Когда сервер разработки работает в режиме разработки (для переменной среды FLASK_ENV установлено значение development), ошибка и данные сохраняются и отображаются в интерактивном отладчике.

Этим поведением можно управлять с помощью конфигурации PRESERVE_CONTEXT_ON_EXCEPTION. Как описано выше, в среде разработки по умолчанию используется значение True.

Не включайте PRESERVE_CONTEXT_ON_EXCEPTION на боевом сервере, так как при исключениях это приведет к утечке памяти в приложении.