FastAPI 的 Depends 到底解决什么?从分页复用到认证
摘要:不从“控制反转”定义讲依赖注入,而是从两个重复问题出发:多个接口如何共享分页规则,私有接口如何复用认证,并在测试中安全替换这些依赖。

图:依赖树统一准备分页、认证等请求级能力,再注入需要它们的路由。
当两个路由都要解析 offset 和 limit,复制参数看起来没有成本;当十个路由都要创建数据库 Session、识别用户、判断权限时,重复就开始制造不一致。
FastAPI 的 Depends 让路由只声明“我需要什么”,框架负责准备并传入。下面从分页和一个教学用 API Key 开始,看看依赖到底如何进入请求链路,以及为什么它会让测试更容易。
先看重复逻辑被拿走后的路由
fastapi dev examples/ch07_dependencies/main.py
curl "http://127.0.0.1:8000/tasks?offset=1&limit=1"
curl -i http://127.0.0.1:8000/tasks/private
curl -i -H "X-API-Key: dev-secret" http://127.0.0.1:8000/tasks/private
没有 Key 时预期 401,正确 Key 时预期 200。
先明确:API Key 只用于观察依赖流程
cd 04-fastapi-beginner
source .venv/bin/activate
打开 依赖示例。API Key 只是为了看清依赖流程,不是生产认证方案;真正的登录链路见 从 Argon2 密码哈希到 JWT。
把分页和认证变成可注入依赖
抽取分页对象
def get_pagination(
offset: Annotated[int, Query(ge=0)] = 0,
limit: Annotated[int, Query(ge=1, le=100)] = 20,
) -> Pagination:
return Pagination(offset=offset, limit=limit)
PaginationDep = Annotated[Pagination, Depends(get_pagination)]
依赖函数拥有与路由函数相同的参数能力。分页校验会进入 OpenAPI,返回对象则注入路由。
抽取认证依赖
def require_api_key(
x_api_key: Annotated[str | None, Header(alias="X-API-Key")] = None,
) -> str:
if x_api_key != "dev-secret":
raise HTTPException(status_code=401, detail="无效的 API Key")
return x_api_key
路由只需要声明 api_key: ApiKeyDep,无需再次读取请求头或重复错误处理。
在测试中替换依赖
app.dependency_overrides[require_api_key] = lambda: "test-key"
try:
response = client.get("/tasks/private")
finally:
app.dependency_overrides.clear()
这让测试关注路由行为,不需要构造真实外部服务或身份系统。

图:分页和认证被抽成可复用依赖,多个路由共享,测试也能替换实现。
请求一次分页,再故意认证失败
curl -i "http://127.0.0.1:8000/tasks?offset=-1"
curl -i -H "X-API-Key: wrong" http://127.0.0.1:8000/tasks/private
curl -i -H "X-API-Key: dev-secret" http://127.0.0.1:8000/tasks/private
python -m pytest tests/test_ch07.py -q
预期依次为 422、401、200,并通过 3 个测试。
FastAPI 会按依赖图准备参数
FastAPI 会构建依赖图:先解析 Query 或 Header,再执行依赖,最后把依赖结果传入路由。依赖还可以依赖其他依赖,因此数据库会话、当前用户、权限校验可以逐层组合。
依赖的价值不只是减少重复,还让前置条件显式出现在函数签名和测试替换点中。普通工具函数仍由代码直接调用;依赖函数由 FastAPI 根据每次请求调度。

图:FastAPI 先准备参数并执行依赖,全部成功后才调用真正的路由函数。
Depends 最常见的五种误用
- 写成
Depends(get_pagination()):这会在导入时调用函数;应传函数本身。 - 依赖中返回敏感值并在响应里回显:示例仅为观察,生产代码不应返回 API Key。
- 测试后不清理 override:后续测试会受到污染。
- 把所有业务函数都改成依赖:依赖适合请求级公共前置条件,不替代领域逻辑。
- 在源码硬编码生产密钥:下一篇使用环境变量。
依赖还可以承载哪些请求级能力
- 增加
RequestIdDep,从X-Request-ID读取请求 ID。 - 让认证依赖再依赖配置对象,而不是硬编码 Key。
- 为 limit=101 增加失败测试。
- 故意不清理 dependency override,观察测试顺序污染。
最后,用测试固定这次改动
手动请求通过后,运行与本文对应的自动化测试:
python -m pytest tests/test_ch07.py -q
python tests/validate_course.py
写在最后
依赖注入为公共请求逻辑提供了稳定接口。下一篇把环境名称、数据库地址和密钥集中成 Settings,并确保敏感配置不会进入响应或版本库。