FastAPI 系列

  1. FastAPI 系列:从第一个接口到生产部署

  2. FastAPI 为什么适合做 JSON API

  3. 第一个接口怎么跑起来

  4. 路径、查询和请求体为什么要分开声明

  5. Pydantic 模型怎么当契约

  6. 路由怎么拆

  7. 依赖注入到底省了什么

  8. 数据库会话怎么进接口

  9. 状态码和异常为什么不要裸 raise

  10. 鉴权怎么接到 Depends 上

  11. 中间件、CORS 和后台任务分别解决什么(当前篇)

  12. 测试怎么写才不连真实库

  13. 生产怎么部署

这三样经常被塞进同一个「横切逻辑」抽屉。它们不在一层:中间件包住整个 ASGI 调用,CORS 是一种专用中间件,后台任务发生在响应已经准备好之后。放错层会出现「预检失败」或「任务用了已关闭的数据库会话」。

请求先过 CORS 与中间件,路由返回后再跑 BackgroundTasks

中间件:请求进出都经过

自定义中间件适合打请求 ID、记耗时、统一安全头。它拿不到 Depends 注入的用户和 DB 会话,所以不适合做业务鉴权。

import time
import uuid

from starlette.middleware.base import BaseHTTPMiddleware


class RequestContextMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        request_id = str(uuid.uuid4())
        start = time.perf_counter()
        response = await call_next(request)
        response.headers["X-Request-ID"] = request_id
        duration_ms = (time.perf_counter() - start) * 1000
        # 把 request_id 和 duration_ms 写入结构化日志
        return response


app.add_middleware(RequestContextMiddleware)

CORS:浏览器跨源时的预检

from fastapi.middleware.cors import CORSMiddleware

origins = [
    "http://localhost:5173",
    "https://www.example.com",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

allow_origins=["*"] 不能和 allow_credentials=True 一起用。生产必须写明白前端 Origin。CORS 只约束浏览器;curl 和服务器间调用不受影响,不能当鉴权。

后台任务:响应后再做轻量工作

from fastapi import BackgroundTasks


def write_audit_log(user_id: int, action: str) -> None:
    path.write_text(f"{user_id} {action}\n")


@app.post("/items/")
async def create_item(item: Item, background_tasks: BackgroundTasks):
    background_tasks.add_task(write_audit_log, item.owner_id, "create_item")
    return item

官方明确:yield 出来的资源在响应发送后就会释放,后台任务不要使用请求级 DB 会话。需要可靠投递时,用 Redis Queue、Celery 或云上的任务队列,而不是 BackgroundTasks。

怎么选

需求

放哪一层

当前用户、DB

Depends

请求 ID、计时、安全头

Middleware

浏览器跨源

CORSMiddleware

发邮件、写审计(可丢失)

BackgroundTasks

必须投递的任务

外部队列

常见误区

用中间件替代 Depends 做登录

OpenAPI 不会出现安全方案,测试也难覆盖单条路由。

CORS 配 * 并带 Cookie

浏览器会直接拒绝。列出确切 Origin。

后台任务里继续用 db: Session

会话在任务跑起来时已经关闭。把主键传进去,任务里自己开会话。

小结

中间件看整段 HTTP,CORS 只服务浏览器,后台任务在响应之后。鉴权和数据库仍留在 Depends。下一篇用 TestClient 把这些层测干净。

参考资料