Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Middleware в FastAPI
Middleware — это функции, которые обрабатывают каждый запрос перед его попаданием в обработчик (handler) и каждый ответ после обработки. Это мощный инструмент для кросс-функциональной логики.
Основная концепция
Middleware работает по принципу луковицы (onion layers):
Запрос → Middleware 1 → Middleware 2 → Handler → Middleware 2 → Middleware 1 → Ответ
Каждый middleware может:
- Модифицировать запрос перед передачей в handler
- Перехватить исключения и вернуть свой ответ
- Модифицировать ответ перед отправкой клиенту
- Выполнить логику как до, так и после обработки запроса
Зачем нужны Middleware
1. Логирование всех запросов и ответов:
from fastapi import FastAPI, Request
import time
import logging
logger = logging.getLogger(__name__)
app = FastAPI()
@app.middleware("http")
async def log_middleware(request: Request, call_next):
start_time = time.time()
# Логируем входящий запрос
logger.info(f"Request: {request.method} {request.url}")
# Передаём в обработчик
response = await call_next(request)
# Логируем время обработки и ответ
process_time = time.time() - start_time
logger.info(f"Response: {response.status_code} (took {process_time:.3f}s)")
# Добавляем header с временем обработки
response.headers["X-Process-Time"] = str(process_time)
return response
2. Аутентификация и авторизация:
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
@app.middleware("http")
async def auth_middleware(request: Request, call_next):
# Пропускаем публичные endpoints
if request.url.path in ["/api/auth/login", "/api/health"]:
return await call_next(request)
# Проверяем токен
token = request.headers.get("Authorization")
if not token or not validate_token(token):
return JSONResponse(
status_code=401,
content={"error": "Unauthorized"}
)
return await call_next(request)
3. Добавление заголовков безопасности:
@app.middleware("http")
async def security_headers_middleware(request: Request, call_next):
response = await call_next(request)
# Добавляем security headers
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
return response
4. CORS (Cross-Origin Resource Sharing):
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://example.com", "https://app.example.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
5. Обработка исключений:
from fastapi.responses import JSONResponse
import traceback
@app.middleware("http")
async def exception_middleware(request: Request, call_next):
try:
response = await call_next(request)
except Exception as exc:
logger.error(f"Unhandled exception: {traceback.format_exc()}")
return JSONResponse(
status_code=500,
content={"error": "Internal Server Error"}
)
return response
6. Добавление контекста к запросу:
from fastapi import Request
import uuid
@app.middleware("http")
async def request_id_middleware(request: Request, call_next):
# Генерируем уникальный ID для запроса
request_id = str(uuid.uuid4())
request.state.request_id = request_id
response = await call_next(request)
response.headers["X-Request-ID"] = request_id
return response
Порядок выполнения Middleware
Мiddleware выполняются в порядке добавления:
app.add_middleware("http", CORSMiddleware) # Выполняется первым
app.add_middleware("http", AuthMiddleware) # Выполняется вторым
app.add_middleware("http", LoggingMiddleware) # Выполняется третьим
Ответы проходят в обратном порядке:
Запрос → CORS → Auth → Logging → Handler
Ответ ← CORS ← Auth ← Logging ← Handler
Встроенные Middleware в FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZIPMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
# GZip сжатие ответов
app.add_middleware(GZIPMiddleware, minimum_size=1000)
# Защита от CSRF и других атак
app.add_middleware(TrustedHostMiddleware, allowed_hosts=["example.com"])
Best Practices
✅ Делай:
- Логируй все важные события
- Обрабатывай исключения на верхнем уровне
- Добавляй security headers
- Используй request_id для трейсирования
- Оптимизируй производительность (избегай тяжелых операций)
❌ Не делай:
- Не выполняй долгие синхронные операции в middleware
- Не обращайся в БД без необходимости
- Не логируй сенситивные данные (пароли, токены)
Пример комплексного middleware
import time
import logging
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
logger = logging.getLogger(__name__)
@app.middleware("http")
async def comprehensive_middleware(request: Request, call_next):
# 1. Добавляем request_id
request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
# 2. Логируем начало
start_time = time.time()
logger.info(f"[{request_id}] {request.method} {request.url.path}")
try:
# 3. Вызываем обработчик
response = await call_next(request)
except Exception as exc:
# 4. Обрабатываем исключение
logger.error(f"[{request_id}] Error: {exc}")
return JSONResponse(status_code=500, content={"error": "Server error"})
# 5. Добавляем headers
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
response.headers["X-Request-ID"] = request_id
# 6. Логируем завершение
logger.info(f"[{request_id}] {response.status_code} ({process_time:.3f}s)")
return response
Вывод
Middleware в FastAPI — это критический инструмент для:
- Безопасности (аутентификация, CSRF защита, security headers)
- Наблюдаемости (логирование, трейсирование, мониторинг)
- Производительности (кеширование, сжатие)
- Обработки ошибок (глобальная обработка исключений)
Правильное использование middleware позволяет держать code чистым и отделить кросс-функциональную логику от бизнес-логики.