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

Выполнение кода до или после запроса во Flask Python

Перехват запросов и освобождение ресурсов приложения Flask

В материале рассмотрены декораторы: @app.before_request(), @app.after_request() и @app.teardown_request(), которые позволяют выполнить определенный код до или после запроса, а так же освободить задействованные ресурсы в случае внезапного исключения/ошибки, при обработке запроса в приложения Flask.

Содержание:


app.before_request(f):

Метод экземпляра приложения app.before_request() регистрирует функцию, которая будет запускаться перед каждым запросом к этому экземпляру приложения. Метод app.before_request() может использоваться в качестве декоратора регистрируемой функции.

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

@app.before_request
def load_user():
    if "user_id" in session:
        g.user = db.session.get(session["user_id"])

Регистрируемая функция будет вызываться без аргументов. Если она возвращает значение, отличное от None, то значение обрабатывается так, как если бы оно было значением, возвращаемым из функции-представления, и дальнейшая обработка запроса прекращается. Другими словами, из этой функции можно возвращать ошибку ответа сервера 500 (или другую), если например, по каким то причинам, соединения с базой данных открыто не было, при этом функция-представление вызываться не будет.

Примечание. Регистрируемые функции в app.before_request будут вызываться для каждого запроса в буквальном смысле, даже при отдаче статики сайта (файлы CSS, JS, и т.д.). Что бы избежать создания множественных подключений к базе данных, необходимо в добавляемой функции в app.before_request исключить имена функций, которые обрабатывают URL не требующие подключения к базе данных.

Смотрим пример:

# например, есть функция, которая обрабатывает 
# отдачу статики из корневой директории сайта
# функция НЕ ТРЕБУЕТ подключения к БД
@app.route('/favicon.svg')
@app.route('/robots.txt')
def static_root():
    return send_from_directory(app.static_folder, request.path[1:])


@app.before_request
def db_connect():
    """Создает соединение с с БД"""
    # проверяем функцию, которая обрабатывает текущий URL
    if request.endpoint not in ['static_root', 'static']:
        # если это не `static_root()` и не `static()` 
        # то создаем подключение к БД
        # (`static()` это внутренняя функция Flask)
        db = getattr(g, 'connect', None)
        if db is None
            g.connect = MySQLdb.connect(**cfg.MYSQLCONF)

app.after_request(f):

Метод экземпляра приложения app.after_request() регистрирует функцию, которая будет запускаться после каждого запроса к этому объекту. Метод app.before_request() может использоваться в качестве декоратора регистрируемой функции.

Регистрируемые функции вызываются с объектом ответа Response и должны возвращать объект ответа. Это позволяет функциям изменять или заменять ответ перед его отправкой. Такое поведение можно использовать, например, что бы установить дополнительные заголовки безопасности в ответ сервера, что бы не изменять ответ в каждой функции-представлении приложения.

@app.after_request
def add_header(response):
    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'SAMEORIGIN'
    response.headers['X-XSS-Protection'] = '1; mode=block'
    return response

Если функция вызывает исключение, то все оставшиеся функции, зарегистрированные app.after_request() вызываться не будут. Следовательно, этот метод не следует использовать для закрытия/освобождения ресурсов (например закрытия соединения с базой данных). Для этого лучше использовать метод app.teardown_request().

app.teardown_request(f):

Метод экземпляра приложения app.teardown_request() регистрирует функцию, которая будет запускаться в конце каждого запроса, независимо от того, было исключение или нет.

Регистрируемые функции выполняются при выталкивании контекста запроса, даже если фактический запрос выполнен не был.

ctx = app.test_request_context()
ctx.push()
...
ctx.pop()

Когда в приведенном выше примере выполняется ctx.pop(), то функции, зарегистрированные в teardown_request() вызываются непосредственно перед выталкиванием контекста запроса из стека активных контекстов. Поведение становится актуальным, если такие конструкции используются в тестах.

Зарегистрированные функции не должны вызывать исключения, т. к. они выполняют код, который может дать сбой! Выполнение кода, который может дать сбой необходимо заключить операторами try/except и регистрировать возникающие ошибки.

Когда зарегистрированные в app.teardown_request() функция была вызвана из-за исключения, ей будет передан объект ошибки.

Возвращаемые значения регистрируемой функций очистки игнорируются.

Примечание. В режиме отладки Flask не будет моментально разрывать запрос из за исключения. При этом он сохранит его живым, чтобы интерактивный отладчик мог получить к нему доступ. Этим поведением можно управлять с помощью переменной конфигурации PRESERVE_CONTEXT_ON_EXCEPTION.

Пример использования app.teardown_request:

@app.before_request
def db_connect():
    """Создает соединение с БД"""
    db = getattr(g, 'connect', None)
    if db is None and request.endpoint not in ['static']:
        g.connect = MySQLdb.connect(**cfg.MYSQLCONF)


@app.teardown_appcontext
def close_connection(exception):
    """Закрывает соединение с БД"""
    db = getattr(g, 'connect', None)
    if db is not None:
        db.close()

app.before_first_request(f):

Внимание! Метод удален в Flask 2.3.0 Вместо него, код инициализации/установки необходимо запускать при создании приложения (в файле __init__.py).

Метод экземпляра приложения app.before_first_request() регистрирует функцию, которая будет запускаться перед первым запросом к этому экземпляру приложения. Метод app.before_first_request() может использоваться в качестве декоратора регистрируемой функции.

Функция будет вызываться без аргументов, а ее возвращаемое значение игнорируется.