В материале рассмотрены декораторы: @app.before_request()
, @app.after_request()
и @app.teardown_request()
, которые позволяют выполнить определенный код до или после запроса, а так же освободить задействованные ресурсы в случае внезапного исключения/ошибки, при обработке запроса в приложения Flask.
app.before_request()
- запускается перед каждым запросом к экземпляру приложения.app.after_request()
- запускается после каждого запроса к экземпляру приложения.app.teardown_request()
- регистрирует функцию, которая будет запускаться в конце каждого запроса.app.before_first_request()
- (удален с версии Flask 2.3.0) запускается перед первым запросом к экземпляру приложения.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()
может использоваться в качестве декоратора регистрируемой функции.
Функция будет вызываться без аргументов, а ее возвращаемое значение игнорируется.