FastAPI 的 Depends 到底解决什么?从分页复用到认证

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

依赖为多个路由复用分页、认证和测试替身

图:依赖树统一准备分页、认证等请求级能力,再注入需要它们的路由。

当两个路由都要解析 offsetlimit,复制参数看起来没有成本;当十个路由都要创建数据库 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 依次解析参数、执行依赖和调用路由

图:FastAPI 先准备参数并执行依赖,全部成功后才调用真正的路由函数。

Depends 最常见的五种误用

  • 写成 Depends(get_pagination()):这会在导入时调用函数;应传函数本身。
  • 依赖中返回敏感值并在响应里回显:示例仅为观察,生产代码不应返回 API Key。
  • 测试后不清理 override:后续测试会受到污染。
  • 把所有业务函数都改成依赖:依赖适合请求级公共前置条件,不替代领域逻辑。
  • 在源码硬编码生产密钥:下一篇使用环境变量。

依赖还可以承载哪些请求级能力

  1. 增加 RequestIdDep,从 X-Request-ID 读取请求 ID。
  2. 让认证依赖再依赖配置对象,而不是硬编码 Key。
  3. 为 limit=101 增加失败测试。
  4. 故意不清理 dependency override,观察测试顺序污染。

最后,用测试固定这次改动

手动请求通过后,运行与本文对应的自动化测试:

python -m pytest tests/test_ch07.py -q
python tests/validate_course.py

写在最后

依赖注入为公共请求逻辑提供了稳定接口。下一篇把环境名称、数据库地址和密钥集中成 Settings,并确保敏感配置不会进入响应或版本库。