Flask の before_request と after_request のライフサイクル

Flask ではリクエスト処理の前後にフック関数を実行できる。before_requestafter_request を使うことで、認証チェック、ロギング、レスポンスの加工などを一元的に処理できる。

リクエストライフサイクル

Flask のリクエスト処理は以下の順序で実行される。

before_request 関数(複数可)
ビュー関数
after_request 関数(複数可)
teardown_request 関数(複数可)

before_request

リクエスト処理の前に実行される。戻り値があれば、ビュー関数は実行されずその値がレスポンスになる。

from flask import Flask, g, request, session

app = Flask(__name__)

@app.before_request
def load_user():
    g.user = None
    if 'user_id' in session:
        g.user = User.query.get(session['user_id'])

@app.before_request
def require_login():
    # 特定のエンドポイントは認証必須
    if request.endpoint in ['profile', 'settings']:
        if g.user is None:
            return redirect(url_for('login'))

複数の before_request は登録順に実行される。

after_request

レスポンス生成後に実行される。レスポンスオブジェクトを受け取り、加工して返す。

@app.after_request
def add_header(response):
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['X-Content-Type-Options'] = 'nosniff'
    return response

@app.after_request
def log_response(response):
    app.logger.info(f'{request.method} {request.path} -> {response.status_code}')
    return response

after_request は登録の逆順で実行される。必ずレスポンスを返すこと。

teardown_request

リクエスト処理の最後に必ず実行される。例外が発生しても実行されるため、リソースのクリーンアップに使う。

@app.teardown_request
def close_db_connection(exception):
    db = g.pop('db', None)
    if db is not None:
        db.close()

引数 exception には例外オブジェクトが渡される(例外がなければ None)。

Blueprint でのフック

Blueprint にもフックを登録できる。Blueprint のフックはその Blueprint 内のルートにのみ適用される。

from flask import Blueprint

api_bp = Blueprint('api', __name__)

@api_bp.before_request
def api_auth():
    # API Blueprint 内のリクエストのみ認証チェック
    token = request.headers.get('Authorization')
    if not verify_token(token):
        return jsonify({'error': 'Unauthorized'}), 401

アプリ全体 vs Blueprint

@app.before_requestすべてのリクエストに適用
@bp.before_requestその Blueprint 内のリクエストのみ
@app.before_request_funcs[None]内部的なリストに直接登録

実行順序の詳細

# 登録順
@app.before_request
def first(): print('1')

@app.before_request
def second(): print('2')

# 実行結果: 1, 2(登録順)

@app.after_request
def after_first(r): print('A'); return r

@app.after_request
def after_second(r): print('B'); return r

# 実行結果: B, A(逆順)

フックの実行順序を把握しておくことで、複雑な前処理・後処理を正しく設計できる。