文章列表
8 分钟阅读

FastAPI 进阶使用指南

更新说明:补充进阶实践、代码块示例和专题阅读路径。


系列:Python Web 框架

第 2 / 11 篇

  1. FastAPI 安装与使用指南
  2. FastAPI 进阶使用指南
  3. Django 安装与使用指南
  4. Django 进阶使用指南
  5. Django 与 FastAPI 对比:如何选择合适的 Python Web 框架
  6. Python Web 框架有哪些,怎么选
  7. Flask 安装与使用指南
  8. Django REST Framework 入门:把 Django 做成 API
  9. FastAPI 项目实战:用户登录与 JWT 鉴权
  10. Python Web 项目部署指南:Gunicorn、Uvicorn、Nginx 和 Docker
  11. Python Web 项目结构怎么设计

FastAPI 安装与使用指南 覆盖了路由、参数校验和自动文档等基础功能。本文继续深入,介绍依赖注入、中间件、后台任务、异步数据库整合、错误处理和生产部署——这些是「把 FastAPI 项目真正上线」必须了解的内容。

本文基于 FastAPI >=0.110.0、Pydantic V2(>=2.7.0)、SQLModel >=0.0.14 的当前(2026 年)技术栈,部分示例可直接用于生产环境。

依赖注入进阶

FastAPI 的依赖注入系统是整个框架的核心。掌握好它,代码的可测试性和复用性会有质的提升。

链式依赖

依赖可以层层嵌套,每个环节独立可测:

dependencies.py
from fastapi import Depends, HTTPException, Security
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
security = HTTPBearer()
async def get_token(
credentials: HTTPAuthorizationCredentials = Security(security),
) -> str:
return credentials.credentials
async def get_current_user(token: str = Depends(get_token)) -> dict:
user = await decode_jwt(token) # 伪代码:解析 JWT
if not user:
raise HTTPException(status_code=401, detail="无效的认证令牌")
return user
async def require_admin(user: dict = Depends(get_current_user)) -> dict:
if user.get("role") != "admin":
raise HTTPException(status_code=403, detail="需要管理员权限")
return user
@app.get("/admin/dashboard")
async def admin_dashboard(user: dict = Depends(require_admin)):
return {"message": f"欢迎,管理员 {user['name']}"}

这种链式设计的好处:每个 Depends 函数职责单一,可以独立测试,也可以在不同路由中组合复用。

类依赖(Callable Class)

当依赖需要持有状态或配置时,用类比函数更灵活:

class PaginationParams:
def __init__(self, max_size: int = 100):
self.max_size = max_size
def __call__(self, page: int = 1, size: int = 20):
if size > self.max_size:
raise HTTPException(
status_code=400,
detail=f"每页最多 {self.max_size} 条",
)
if page < 1:
raise HTTPException(status_code=400, detail="page 必须 >= 1")
return {"page": page, "size": size, "offset": (page - 1) * size}
@app.get("/items")
async def list_items(pagination: dict = Depends(PaginationParams(max_size=50))):
# pagination = {"page": 1, "size": 20, "offset": 0}
...

也可用于封装数据库连接、RPC 客户端等有状态资源。

请求作用域缓存

FastAPI 在同一请求内只会执行一次依赖(默认 use_cache=True):

def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
def get_current_user(db=Depends(get_db), token=Depends(get_token)):
...
# get_db 和 get_token 分别在本次请求中只执行一次
@app.get("/profile")
def profile(db=Depends(get_db), user=Depends(get_current_user)):
...

如果你需要每次调用都执行,设置 Depends(func, use_cache=False)

测试时覆盖依赖

FastAPI 提供了 dependency_overrides,测试时不需要 mock/patch:

from fastapi.testclient import TestClient
app.dependency_overrides[get_current_user] = lambda: {
"id": 1, "name": "测试用户", "role": "admin"
}
client = TestClient(app)
def test_admin_dashboard():
response = client.get("/admin/dashboard")
assert response.status_code == 200
assert "测试用户" in response.json()["message"]

这是 FastAPI 测试哲学的核心:用依赖注入替代 mock,测试代码和业务代码一样干净。

中间件

请求 ID 中间件

每个请求分配唯一 ID,方便日志追踪和问题排查:

import uuid
from starlette.middleware.base import BaseHTTPMiddleware
class RequestIDMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
request_id = request.headers.get("X-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
app.add_middleware(RequestIDMiddleware)

请求计时中间件

import time
class TimingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
start = time.perf_counter()
response = await call_next(request)
elapsed = (time.perf_counter() - start) * 1000
response.headers["X-Process-Time-Ms"] = f"{elapsed:.2f}"
return response

注意:BaseHTTPMiddleware 的性能开销

BaseHTTPMiddleware 基于 StreamingResponse,每次请求会引入额外的数据复制。对于极高吞吐场景,建议用纯 ASGI 中间件:

class ASGITimingMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
return await self.app(scope, receive, send)
start = time.perf_counter()
async def send_wrapper(message):
if message["type"] == "http.response.start":
elapsed = (time.perf_counter() - start) * 1000
headers = list(message.get("headers", []))
headers.append((b"x-process-time-ms", f"{elapsed:.2f}".encode()))
message["headers"] = headers
await send(message)
await self.app(scope, receive, send_wrapper)

纯 ASGI 中间件零额外开销,适合高并发场景。

Lifespan 事件

FastAPI 已弃用 @app.on_event("startup")@app.on_event("shutdown"),推荐使用 lifespan

lifespan.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动:初始化数据库连接池、Redis 客户端、HTTP 客户端等
app.state.db_pool = await create_db_pool()
app.state.redis = await create_redis_client()
app.state.http_client = httpx.AsyncClient()
yield
# 关闭:释放资源
await app.state.db_pool.close()
await app.state.redis.close()
await app.state.http_client.aclose()
app = FastAPI(lifespan=lifespan)

从旧事件钩子迁移时,核心变化是把启动和关闭逻辑收敛到同一个上下文管理器:

lifespan-migration.diff
@app.on_event("startup")
async def startup():
app.state.redis = await create_redis_client()
@app.on_event("shutdown")
async def shutdown():
await app.state.redis.close()
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.redis = await create_redis_client()
yield
await app.state.redis.close()
app = FastAPI(lifespan=lifespan)

在路由中通过 request.app.state 访问这些资源:

@app.get("/cache/{key}")
async def get_cache(key: str, request: Request):
value = await request.app.state.redis.get(key)
return {"key": key, "value": value}

异步数据库整合

FastAPI 的推荐数据库方案是 SQLModel(由 FastAPI 作者开发,融合了 SQLAlchemy 和 Pydantic):

统一模型定义

SQLModel 的一个模型同时是数据库表定义、请求校验和响应序列化:

from sqlmodel import SQLModel, Field
# 数据库模型
class Post(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
title: str = Field(index=True)
body: str
published: bool = Field(default=False)
# 请求体(只暴露需要客户端填写的字段)
class PostCreate(SQLModel):
title: str = Field(..., min_length=1, max_length=200)
body: str
published: bool = False
# 响应体
class PostPublic(SQLModel):
id: int
title: str
published: bool

异步引擎 + 事务管理

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlmodel.ext.asyncio.session import AsyncSession
from collections.abc import AsyncGenerator
engine = create_async_engine(
"postgresql+asyncpg://user:pass@localhost/dbname",
pool_pre_ping=True,
pool_recycle=300,
)
async def get_session() -> AsyncGenerator[AsyncSession, None]:
async with AsyncSession(engine) as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise

这个依赖函数统一管理事务:路由正常返回时自动 commit(),异常时 rollback()。所有写操作只需要 session.add() / session.flush(),不需要手动提交。

分层架构

生产项目建议按领域分层(由 Netflix Dispatch 团队推广的模式):

src/
├── posts/
│ ├── router.py # HTTP 层
│ ├── service.py # 业务逻辑
│ ├── repository.py # 数据访问
│ ├── schemas.py # Pydantic 模型
│ └── models.py # SQLModel 表定义
├── users/
│ └── ...
├── dependencies.py # 全局依赖
├── database.py # 引擎和会话
└── main.py # 应用入口

各层职责:

返回类型异常
RouterPydantic Schema不抛异常,调用 Service
ServicePydantic SchemaDomainException
RepositoryModel 或 None不抛异常,只做数据访问

示例:

repository.py
class PostRepository:
async def get_by_id(self, session: AsyncSession, post_id: int) -> Post | None:
return await session.get(Post, post_id)
async def list_all(self, session: AsyncSession, offset: int, limit: int) -> list[Post]:
result = await session.execute(
select(Post).offset(offset).limit(limit).order_by(Post.id.desc())
)
return list(result.scalars().all())
# service.py
class PostService:
def __init__(self, repo: PostRepository = Depends()):
self.repo = repo
async def get_post(self, session: AsyncSession, post_id: int) -> PostPublic:
post = await self.repo.get_by_id(session, post_id)
if post is None:
raise NotFoundError("文章", post_id)
return PostPublic.model_validate(post)
# router.py
router = APIRouter(prefix="/posts", tags=["文章"])
@router.get("/{post_id}", response_model=PostPublic)
async def get_post(
post_id: int,
session: AsyncSession = Depends(get_session),
service: PostService = Depends(),
):
return await service.get_post(session, post_id)

这里 PostPublic.model_validate(post) 是 Pydantic V2 的新写法,替代旧的 .from_orm()

后台任务与任务队列

BackgroundTasks(轻量级)

适合发邮件、写日志等不关心结果的异步操作:

from fastapi import BackgroundTasks
def send_verification_email(email: str, code: str):
# 同步函数会在线程池中执行
...
@app.post("/register")
async def register(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(send_verification_email, email, "123456")
return {"message": "注册成功,验证邮件已发送"}

局限性:没有重试机制、不持久化、服务重启即丢失。

任务队列(生产级)

对于需要重试、调度、监控的任务,使用 Celery / ARQ / Dramatiq

# 使用 ARQ(异步优先的任务队列,和 FastAPI 配合更好)
from arq import create_pool
from arq.jobs import Job
async def send_report(user_id: int):
# 耗时操作
...
@app.post("/reports/generate")
async def generate_report(user_id: int, request: Request):
redis = request.app.state.redis # ARQ 也基于 Redis
job = await redis.enqueue_job("send_report", user_id)
return {"job_id": job.job_id, "status": "queued"}
特性BackgroundTasksARQ / Celery
重试不支持支持
持久化不支持支持
进度追踪不支持支持
定时任务不支持支持
适用场景发邮件、记日志报表生成、数据处理、定时任务

结构化错误处理

定义统一的异常体系,让 API 错误响应一致:

from fastapi import Request
from fastapi.responses import JSONResponse
class AppError(Exception):
def __init__(self, message: str, code: str, status_code: int = 400):
self.message = message
self.code = code
self.status_code = status_code
class NotFoundError(AppError):
def __init__(self, resource: str, resource_id: int):
super().__init__(
message=f"{resource}(id={resource_id})未找到",
code="RESOURCE_NOT_FOUND",
status_code=404,
)
class ConflictError(AppError):
def __init__(self, message: str):
super().__init__(message=message, code="RESOURCE_CONFLICT", status_code=409)
@app.exception_handler(AppError)
async def app_error_handler(request: Request, exc: AppError):
return JSONResponse(
status_code=exc.status_code,
content={
"error": {
"code": exc.code,
"message": exc.message,
"request_id": getattr(request.state, "request_id", None),
}
},
)
@app.exception_handler(ValidationError)
async def validation_error_handler(request: Request, exc: ValidationError):
return JSONResponse(
status_code=422,
content={
"error": {
"code": "VALIDATION_ERROR",
"message": "请求参数校验失败",
"details": exc.errors(),
"request_id": getattr(request.state, "request_id", None),
}
},
)

现在业务代码中直接 raise NotFoundError("文章", 42),客户端会收到结构一致的错误响应,包含 request_id 方便定位问题。

WebSocket 支持

FastAPI 原生支持 WebSocket,适合实时推送场景:

from fastapi import WebSocket, WebSocketDisconnect
class ConnectionManager:
def __init__(self):
self.active_connections: list[WebSocket] = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)
manager = ConnectionManager()
@app.websocket("/ws/chat")
async def chat(websocket: WebSocket):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
await manager.broadcast(f"收到消息: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)

性能优化清单

根据 2026 年社区实践,性能优化的优先顺序:

  1. 用纯 ASGI 中间件替代 BaseHTTPMiddleware:减少每请求的 StreamingResponse 开销
  2. 使用 Pydantic V2:底层 Rust 实现,校验速度比 V1 快 5-50 倍
  3. 数据库连接池调优pool_sizemax_overflow 按实际负载设置
  4. 为高频读接口加 Redis 缓存:减少数据库压力
  5. 正确区分 async defdef
    • I/O 密集(数据库、HTTP 调用)→ async def + await
    • 同步 SDK 无法 await → def(FastAPI 在线程池中执行),或 asyncio.to_thread()
    • CPU 密集 → 不要在线程池跑,用 Celery/ARQ 推到独立 Worker
import asyncio
# 在线程池中执行同步的 CPU 密集型操作
result = await asyncio.to_thread(process_image_sync, image_bytes)

生产部署检查清单

  • Lifespan 管理资源:数据库连接池、Redis、HTTP 客户端都在 lifespan 中创建和销毁
  • 结构化日志:每个请求携带 request_id,用 structlogloguru 输出 JSON 格式日志
  • UV / Ruff:用 ruff 做 linting 和格式化,uv 管理依赖
  • 异步测试:用 httpx.AsyncClient + ASGITransport 写异步测试,用 dependency_overrides 隔离依赖
  • 健康检查端点/health 返回数据库和 Redis 连通状态,供负载均衡探测
  • CORS 限制:生产环境 allow_origins 指定具体域名,不要用 ["*"]
  • 速率限制:用 slowapi 或 Redis + 自定义中间件做 API 限流
  • 静态资源:交给 Nginx 或 CDN,不要用 FastAPI 直接 serve
Terminal window
# 生产环境启动命令
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 --log-level info

也可以配合 Gunicorn 管理进程:

Terminal window
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000

常见问题

Pydantic V2 迁移

V2 有几个不兼容变更需要注意:

# 旧 .from_orm() → 新 .model_validate()
user = UserSchema.model_validate(orm_instance)
# 旧 @validator → 新 @field_validator
from pydantic import field_validator
@field_validator("password")
@classmethod
def check_strength(cls, v): ...
# 旧 dict() → 新 model_dump()
data = user.model_dump()

async def 路由中调用了同步 ORM

如果你的 ORM 不支持异步,有两个选择:

  1. 将路由改为 def(FastAPI 会自动在线程池中运行)
  2. 使用 asyncio.to_thread() 包装同步调用:
@app.get("/users/{user_id}")
async def get_user(user_id: int):
user = await asyncio.to_thread(sync_get_user_from_db, user_id)
return user

数据库会话在响应阶段已被关闭

不要在响应模型的 computed_fieldfield_serializer 中访问数据库——此时会话可能已关闭。确保所有数据查询在 Service 层完成,Schema 只做纯数据转换。

项目结构混乱

早期不要过度设计。随着代码量增长,按以下节奏演进:

  • 单文件 (< 10 路由) → main.py 就够了
  • 10-30 路由 → 拆 routers/ + schemas.py
  • 30+ 路由 → 按领域拆包 + Service/Repository 分层

总结

FastAPI 的进阶用法围绕几个核心模式展开:

  • 依赖注入:链式依赖 + 类依赖 + dependency_overrides 测试,是 FastAPI 最重要的设计模式
  • 中间件:请求 ID、计时用纯 ASGI 中间件,避免 BaseHTTPMiddleware 的性能开销
  • Lifespan:替代已弃用的 on_event,统一管理资源生命周期
  • SQLModel:一个模型同时做 ORM 表定义、请求校验和响应序列化,减少重复代码
  • 分层架构:Router → Service → Repository,各层职责清晰
  • 结构化异常:自定义异常 + 全局异常处理器,让错误响应一致、可追踪

掌握这些模式后,FastAPI 项目可以从原型平滑演进到生产级服务。需要补齐登录闭环时,可以继续看 FastAPI 项目实战:用户登录与 JWT 鉴权;如果项目已经开始变大,Python Web 项目结构怎么设计 会更适合接着读。更多细节参考 FastAPI 官方文档FastAPI Best Practices