Request Pre and Post-processing Hooks
Flask provides a set of decorators to hook into the request lifecycle, allowing you to execute code before a request is handled, after a response is generated, or when a request context is torn down. These hooks are defined in the Scaffold class, which serves as the base for both the Flask application object and Blueprint objects.
Executing Code Before the Request
When you need to perform actions like loading a user from a session or checking permissions before every request, use the before_request decorator.
from flask import Flask, g, session
app = Flask(__name__)
@app.before_request
def load_user():
if "user_id" in session:
# Assume db.get_user is a helper function
g.user = db.get_user(session["user_id"])
Short-circuiting the Request
If a before_request function returns a non-None value, Flask treats that value as the response and stops further processing. This means the view function and any subsequent before_request handlers will not be called.
@app.before_request
def check_maintenance_mode():
if is_maintenance_mode():
return "Service Unavailable", 503
Internally, Flask.preprocess_request iterates through the registered functions in self.before_request_funcs. It calls the application-level hooks first, followed by hooks for the active blueprint.
Modifying the Response
To modify the response object after the view function has executed, use the after_request decorator. This is commonly used to add custom headers or security policies to every response.
@app.after_request
def add_security_headers(response):
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "SAMEORIGIN"
return response
Requirements and Execution Order
- Return Value: An
after_requestfunction must receive a response object and must return a response object (either the same one or a new one). - Reverse Order: Functions registered with
after_requestare executed in reverse order of registration. If you registerAthenB,Bwill run first. - Exceptions: These hooks are only called if the request was handled successfully without an unhandled exception. If an exception occurs, use
teardown_requestfor cleanup.
In Flask.process_response, Flask first executes hooks added dynamically via after_this_request, then blueprint-specific hooks, and finally application-wide hooks.
Cleaning Up Resources
The teardown_request decorator registers functions to run when the request context is popped. This happens at the very end of a request, even if an unhandled exception occurred. This makes it the ideal place for resource cleanup, such as closing database connections.
@app.teardown_request
def close_db_connection(exception=None):
db.close()
if exception:
app.logger.error(f"Request failed with: {exception}")
Key Characteristics
- Exception Handling: The function receives one argument: the exception that caused the request to fail, or
Noneif it succeeded. - Guaranteed Execution: Unlike
after_request, teardown functions are called even if abefore_requesthook short-circuited the request or if the view raised an error. - Reverse Order: Like
after_request, these run in reverse order of registration. - Reliability: Flask's
do_teardown_requestmethod wraps each call in atry/exceptblock (via_CollectErrors) to ensure that one failing teardown function does not prevent others from running.
Blueprint-Specific vs. Global Hooks
Blueprints allow you to define hooks that only apply to the routes they manage.
- Blueprint-only:
@bp.before_requestonly runs for requests handled by that blueprint. - Application-wide from Blueprint:
@bp.before_app_request(and its counterpartsafter_app_requestandteardown_app_request) registers the hook on the application itself, affecting every request regardless of which blueprint handles it.
auth_bp = Blueprint("auth", __name__)
@auth_bp.before_request
def check_auth_token():
# Only runs for routes in auth_bp
...
@auth_bp.before_app_request
def global_init():
# Runs for every request in the app
...
Asynchronous Hooks
Flask supports asynchronous request hooks. You can define any of these hooks as async def, and Flask will handle the execution using ensure_sync.
@app.before_request
async def async_before():
await some_async_setup()
@app.after_request
async def async_after(response):
await some_async_logging(response)
return response
When an async hook is called, Flask ensures it is executed within the appropriate context, allowing you to use await for I/O-bound tasks during the request lifecycle.