Короткий ответ
WSGI-middleware оборачивает сам вызываемый объект app.wsgi_app, получая доступ к «сырому» WSGI-окружению до того, как Flask начнёт маршрутизацию и построит объект request; before_request/after_request работают уже внутри контекста запроса Flask и имеют доступ к высокоуровневым объектам фреймворка.
Как это работает подробнее
WSGI-приложение — это просто вызываемый объект (environ, start_response) -> iterable. Оборачивая app.wsgi_app в свою функцию/класс, можно модифицировать environ до того, как Flask его обработает, или обернуть ответ на самом низком уровне — это универсально и работает с любым WSGI-совместимым фреймворком, не только Flask.
Пример кода
example.py
class StripPrefixMiddleware:
def __init__(self, app, prefix):
self.app = app
self.prefix = prefix
def __call__(self, environ, start_response):
path = environ.get("PATH_INFO", "")
if path.startswith(self.prefix):
environ["PATH_INFO"] = path[len(self.prefix):]
return self.app(environ, start_response)
# wsgi.py
from myapp import app as flask_app
app = StripPrefixMiddleware(flask_app.wsgi_app, "/api")
flask_app.wsgi_app = appЧто использовать на практике
- Практика. WSGI middleware обычно используют для инфраструктурных задач независимо от фреймворка (например, проксирование заголовков от балансировщика, сжатие gzip через стороннюю библиотеку, базовая авторизация на уровне инфраструктуры).
before_request/after_requestудобнее для логики, завязанной на Flask-специфичные объекты (g,session, маршрутизация Blueprint).
Подводные камни
- Риск. Пытаются в WSGI middleware обращаться к Flask-специфичным вещам типа
flask.request, которые на этом уровне ещё не существуют; забывают, что изменениеenvironв WSGI middleware должно происходить до вызоваself.app(environ, start_response), а не после.