两周时间,4 个项目。从"Hello World"到能交付的后端服务,FastAPI 的上手曲线确实友好——但如果没人告诉你那些隐藏在文档角落的坑,你大概率会一个个踩过去。这篇文章就是一份「避坑指南」,覆盖我在两个星期里踩过的所有印象深刻的问题。
项目一:REST API — 依赖注入的过度使用陷阱
FastAPI 的依赖注入系统 Depends() 非常强大,但也容易滥用。最典型的错误是把所有东西都塞进依赖链里:
# ❌ 嵌套过深,难以调试
@router.get("/items")
async def get_items(
db: Session = Depends(get_db),
user: User = Depends(get_current_user),
cache: Redis = Depends(get_redis),
limiter: RateLimiter = Depends(get_rate_limiter),
):
当依赖链超过三层时,调试成本急剧上升——一个 Depends() 内部的异常会被 FastAPI 包装后抛出,堆栈信息几乎不可读。我的建议是:将依赖注入限制在真正需要复用的横切关注点上,比如认证、数据库会话;业务逻辑不要放进 Depends(),而是直接写在路由函数中或提取为独立的 Service 层。
项目二:WebSocket 服务 — 连接管理的心跳机制
WebSocket 看起来简单——客户端连上来,服务端推送数据。但实际上,连接随时可能断开:用户的网络切换、代理服务器超时、移动端锁屏……没有心跳机制的生产级 WebSocket 服务,就是在等事故。
# WebSocket 心跳实现
import asyncio
async def heartbeat(ws: WebSocket, interval: int = 30):
while True:
try:
await asyncio.sleep(interval)
await ws.send_json({"type": "ping"})
except Exception:
break # 连接已断开
另外需要特别注意:不要把阻塞操作放进 WebSocket 的事件循环中。比如使用 psutil.cpu_percent(interval=1) 会阻塞整个事件循环 1 秒,导致其他 WebSocket 连接全部暂停。任何可能阻塞的操作都应该用 asyncio.to_thread() 或 run_in_executor() 包装。
项目三:文件上传系统 — 流式处理 vs 内存缓冲
FastAPI 的 UploadFile 默认会使用 SpooledTemporaryFile——小文件缓存在内存,大文件写入临时磁盘。但这个「小文件」的阈值是 1MB,当你接收几百个并发的 900KB 文件上传时,内存依然会爆炸。
# ✅ 强制流式写入,避免内存堆积
from pathlib import Path
@router.post("/upload")
async def upload(file: UploadFile):
dest = Path("uploads") / file.filename
with dest.open("wb") as buffer:
while chunk := await file.read(64 * 1024): # 64KB 分块
buffer.write(chunk)
return {"filename": file.filename}
分块读取 + 立即写入磁盘,确保无论文件多大、并发多少,内存占用始终可控。另外别忘了用 .dockerignore 排除上传目录,否则每次构建镜像都会把这些文件打进镜像。
项目四:认证服务 — 中间件挂载顺序和 CORS 陷阱
认证系统的坑不在于 JWT 本身,而在于 FastAPI 中间件的注册顺序。FastAPI 的中间件是洋葱模型——先注册的先执行(外层),后注册的后执行(内层)。
# 正确的中间件顺序
app.add_middleware(CORSMiddleware, ...) # 最外层:CORS
app.add_middleware(TrustedHostMiddleware, ...)
app.add_middleware(AuthMiddleware, ...) # 最内层:认证
如果把 CORSMiddleware 放在认证中间件之后,OPTIONS 预检请求就会被拦截,浏览器直接报 CORS 错误——而你在服务器日志里完全看不到问题。
另一个常见错误是生产环境中把 allow_origins 设成 ["*"]——这在 FastAPI 中会导致 allow_credentials=True 静默失效,Cookie 形式的认证令牌不会被浏览器发送。生产环境应该明确列出允许的域名。
Pydantic v2 的性能影响
如果你从 Pydantic v1 迁移到 v2,模型验证的速度提升是显著的(官方宣称 5-50 倍)。但迁移并不总是平滑的——schema_extra 变成了 json_schema_extra,@validator 变成了 @field_validator,而且在某些边界情况下 v2 的 strict mode 行为与 v1 不同。建议迁移前先完整跑一遍测试套件。
总结
FastAPI 的「快乐路径」非常短——一个下午就能搭出可用的 API。但生产环境的可靠性来自于对细节的打磨:心跳、流控、超时、优雅关闭。希望这 4 个项目的教训能帮你少走一些弯路。