Middleware, CORS и фоновые задачи
Middleware оборачивает каждый запрос сквозной логикой (логирование, заголовки), CORS разрешает браузерным фронтендам обращаться к API, а BackgroundTasks выполняет работу после отправки ответа.
Три инструмента «по краям» запроса: middleware работает до и после обработчика для всех запросов, CORS — частный, но критичный middleware для браузеров, а фоновые задачи откладывают неважное на «после ответа».
Некоторая логика не привязана к конкретному эндпоинту: засечь время обработки, добавить заголовок, залогировать запрос. Это работа middleware — слоя, через который проходит каждый запрос по пути к обработчику и каждый ответ на пути обратно. Middleware образуют «луковицу»: запрос проходит слои внутрь до обработчика, ответ — наружу в обратном порядке.
from fastapi import FastAPI, Request, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
import time
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://my-frontend.app"],
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def add_timing(request: Request, call_next):
start = time.perf_counter()
response = await call_next(request) # вызываем следующий слой/обработчик
response.headers["X-Process-Time"] = str(time.perf_counter() - start)
return response
@app.post("/signup")
async def signup(email: str, tasks: BackgroundTasks):
tasks.add_task(send_welcome_email, email) # выполнится ПОСЛЕ ответа
return {"status": "ok"}
CORS — особый случай. Браузер по умолчанию запрещает странице с одного домена обращаться к API на другом; чтобы разрешить, сервер должен прислать нужные заголовки. CORSMiddleware делает это, и без него фронтенд получит загадочную ошибку запроса. BackgroundTasks же решает другую проблему: отправку письма не стоит заставлять ждать пользователя — ответ уходит сразу, а письмо шлётся фоном.
Как работает под капотом
Middleware — это вложенные обёртки: каждый слой получает запрос, может что-то сделать до, вызвать следующий слой и что-то сделать после. Смоделируем «луковицу» на stdlib:
def handler(request):
return f"ответ на {request}"
def timing_mw(next_layer):
def wrapper(request):
print(" [timing] до обработки")
result = next_layer(request)
print(" [timing] после обработки")
return result
return wrapper
def logging_mw(next_layer):
def wrapper(request):
print("[log] входящий запрос:", request)
result = next_layer(request)
print("[log] исходящий ответ:", result)
return result
return wrapper
# собираем «луковицу»: log -> timing -> handler
app = logging_mw(timing_mw(handler))
print("РЕЗУЛЬТАТ:", app("GET /items"))
Попробуй сам ▶ Порядок логов показывает «луковицу»: внешний слой (log) входит первым и выходит последним, ровно как стек middleware в FastAPI.
Частые ошибки
Первая — забыть CORS и долго искать, почему фронтенд не может достучаться до API (ошибка видна только в браузере). Вторая — ставить allow_origins=["*"] вместе с allow_credentials=True, что небезопасно и не работает по спецификации. Третья — класть в BackgroundTasks критичную работу: если процесс упадёт, задача потеряется (для важного нужна очередь вроде Celery). Четвёртая — тяжёлая блокирующая логика в middleware, тормозящая все запросы.
Best practices
- Настраивайте CORS явно: конкретные
allow_origins, а не*с credentials. - В middleware — лёгкая сквозная логика (тайминги, заголовки, корреляция логов).
BackgroundTasks— для некритичных пост-действий (письма, уведомления); для важного — внешняя очередь.- Помните про порядок: middleware образуют стек, внешний срабатывает первым на вход.
Middleware против зависимостей: что выбрать
И middleware, и зависимости умеют выполнять код «вокруг» обработки, и новички путаются, что применять. Разница в области действия и доступе к данным. Middleware работает на уровне сырого запроса и ответа, видит их целиком, применяется ко всем маршрутам без разбора и идеален для сквозных задач: логирование, тайминги, добавление заголовков, корреляционные идентификаторы. Но он не знает про разобранные параметры и модели — для него тело это просто байты. Зависимость, наоборот, встроена в конкретный обработчик, видит типизированные параметры, участвует в документации и легко подменяется в тестах — она для логики, специфичной для эндпоинта: авторизация, выдача сессии, проверка прав. Правило: общее и низкоуровневое для всех запросов — middleware; типизированное и точечное для конкретных маршрутов — зависимость. Понимание этой границы избавляет от попыток затолкать бизнес-проверки в middleware, где у вас нет ни типов, ни удобной тестируемости.
Итог: middleware даёт сквозную логику «луковицей» вокруг каждого запроса, CORS разрешает доступ браузерным фронтендам, а BackgroundTasks переносит некритичную работу на время после ответа. Для гарантированных фоновых задач используйте настоящую очередь.