Managing the Request Lifecycle
The request lifecycle in this codebase is managed through a series of hooks that allow developers to execute code before a request is dispatched, after a response is generated, and during the final cleanup phase. These hooks are defined in the Scaffold class (found in src/flask/sansio/scaffold.py), which serves as the base for both the main Flask application and Blueprint objects.
Preprocessing with before_request
The before_request hook allows you to run functions before the view function is executed. This is commonly used for opening database connections, loading user data into the g object, or performing permission checks.
Short-Circuiting
A key feature of before_request is its ability to "short-circuit" the request. If a before_request function returns a non-None value, that value is treated as the response, and the view function (and any subsequent before_request hooks) will not be called.
As seen in tests/test_basic.py, multiple hooks can be registered, and they execute in the order they were defined:
@app.before_request
def before_request1():
evts.append(1)
@app.before_request
def before_request2():
evts.append(2)
return "hello" # Short-circuits here
@app.before_request
def before_request3():
evts.append(3) # This will never run
return "bye"
@app.route("/")
def index():
return "view" # This will never run
In the Flask class, this logic is implemented in preprocess_request (in src/flask/app.py), which iterates through the registered functions and returns the first non-None result.
Post-processing with after_request
The after_request hook is used to modify or replace the response object after the view function has finished. Unlike before_request, these functions must accept a response object as an argument and must return a response object.
Execution Order
Functions registered with after_request are executed in reverse order of registration. This allows hooks registered later to wrap or modify the results of earlier hooks.
@app.after_request
def add_header(response):
response.headers["X-Custom"] = "Value"
return response
@app.after_request
def modify_data(response):
response.data += b"|modified"
return response
Exception Behavior
It is important to note that after_request functions are not called if an unhandled exception occurs during the request. If you need code to run regardless of success or failure, use teardown_request.
Cleanup with teardown_request
The teardown_request hook is designed for resource management, such as closing database connections or file handles. These functions are called when the request context is popped, which typically happens at the very end of the request lifecycle.
Guaranteed Execution
Teardown functions are guaranteed to run even if an exception was raised during the request. They receive an exception object as an argument, which will be None if the request was successful.
@app.teardown_request
def close_db(exc):
if exc:
app.logger.error(f"Request failed with: {exc}")
db.close()
In src/flask/app.py, the do_teardown_request method ensures robustness by using a _CollectErrors utility. This ensures that even if one teardown function raises an error, all other registered teardown functions are still executed.
Scoping and Execution Order
Hooks can be registered globally on the Flask app or locally on a Blueprint. The execution order depends on the type of hook:
- before_request: Executed from the application level first, then the blueprint level.
- after_request: Executed from the blueprint level first, then the application level (both in reverse registration order).
- teardown_request: Executed from the blueprint level first, then the application level (both in reverse registration order).
This hierarchy is managed in src/flask/app.py by methods like preprocess_request and process_response, which aggregate hooks from both the app and the active blueprints using the req.blueprints list.
Summary of Lifecycle Hooks
| Hook | Timing | Can Short-circuit? | Called on Exception? |
|---|---|---|---|
before_request | Before view | Yes | No |
after_request | After view | No | No |
teardown_request | Context pop | No | Yes |