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

中间件:请求进出都经过
自定义中间件适合打请求 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。
怎么选
常见误区
用中间件替代 Depends 做登录
OpenAPI 不会出现安全方案,测试也难覆盖单条路由。
CORS 配 * 并带 Cookie
浏览器会直接拒绝。列出确切 Origin。
后台任务里继续用 db: Session
会话在任务跑起来时已经关闭。把主键传进去,任务里自己开会话。
小结
中间件看整段 HTTP,CORS 只服务浏览器,后台任务在响应之后。鉴权和数据库仍留在 Depends。下一篇用 TestClient 把这些层测干净。
参考资料
中间件、CORS 和后台任务分别解决什么
https://lautung.com/archives/fastapi-10-middleware
评论