Flask は WSGI アプリケーションであり、WSGI ミドルウェアを使ってリクエスト/レスポンスの処理をカスタマイズできる。ミドルウェアはアプリケーションをラップし、前処理・後処理を挟む。
WSGI の基本
WSGI アプリケーションは callable で、environ と start_response を受け取る。
def simple_app(environ, start_response):
status = '200 OK'
headers = [('Content-Type', 'text/plain')]
start_response(status, headers)
return [b'Hello, World!']
Flask アプリも内部的にはこの形式である。
基本的なミドルウェア
class SimpleMiddleware:
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
# 前処理
print(f"Request: {environ['PATH_INFO']}")
# 元のアプリケーションを呼び出す
return self.app(environ, start_response)
# Flask アプリにミドルウェアを適用
app.wsgi_app = SimpleMiddleware(app.wsgi_app)
app.wsgi_app をラップすることで、Flask のルーティングより前に処理を挟める。
レスポンスを加工するミドルウェア
class ResponseTimeMiddleware:
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
import time
start = time.time()
def custom_start_response(status, headers, exc_info=None):
elapsed = time.time() - start
headers.append(('X-Response-Time', f'{elapsed:.3f}s'))
return start_response(status, headers, exc_info)
return self.app(environ, custom_start_response)
app.wsgi_app = ResponseTimeMiddleware(app.wsgi_app)
IP フィルタリングミドルウェア
class IPFilterMiddleware:
def __init__(self, app, blocked_ips):
self.app = app
self.blocked_ips = blocked_ips
def __call__(self, environ, start_response):
remote_addr = environ.get('REMOTE_ADDR', '')
if remote_addr in self.blocked_ips:
status = '403 Forbidden'
headers = [('Content-Type', 'text/plain')]
start_response(status, headers)
return [b'Access Denied']
return self.app(environ, start_response)
app.wsgi_app = IPFilterMiddleware(app.wsgi_app, {'192.168.1.100'})
Werkzeug の組み込みミドルウェア
Werkzeug にはいくつかのミドルウェアが用意されている。
from werkzeug.middleware.proxy_fix import ProxyFix
from werkzeug.middleware.profiler import ProfilerMiddleware
# リバースプロキシ対応
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)
# プロファイリング
app.wsgi_app = ProfilerMiddleware(app.wsgi_app)
ミドルウェア vs before_request
| ミドルウェア | WSGI レベル、Flask の外側で動作 |
| before_request | Flask レベル、コンテキストが利用可能 |
ミドルウェアは Flask のコンテキストにアクセスできないが、より低レベルな制御が可能である。単純な前処理・後処理なら before_request/after_request のほうが扱いやすい。
複数ミドルウェアの適用順序
app.wsgi_app = MiddlewareA(app.wsgi_app)
app.wsgi_app = MiddlewareB(app.wsgi_app)
# リクエスト時: B → A → Flask → A → B
最後に適用したミドルウェアが最初に実行される。