34. FastAPI 依赖注入_嵌套依赖
依赖也可以有自己的依赖
“校验令牌 → 找到用户 → 确认用户启用 → 确认管理员”是一条依赖链。嵌套依赖把前后关系表达出来,避免每个接口都手工重复实现整个流程。
用演示令牌观察执行链
python
from typing import Annotated
from fastapi import Depends, FastAPI, Header, HTTPException
app = FastAPI()
USERS = {"demo-token": {"id": 1, "name": "管理员", "active": True,
"roles": ["admin"]}}
def read_token(authorization: Annotated[str | None, Header()] = None):
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(401, "需要 Bearer 令牌")
return authorization.removeprefix("Bearer ")
def current_user(token: Annotated[str, Depends(read_token)]):
user = USERS.get(token)
if user is None:
raise HTTPException(401, "无效令牌")
return user
def active_user(user: Annotated[dict, Depends(current_user)]):
if not user["active"]:
raise HTTPException(403, "用户已停用")
return user
def admin_user(user: Annotated[dict, Depends(active_user)]):
if "admin" not in user["roles"]:
raise HTTPException(403, "没有管理员权限")
return user
@app.get("/me")
def me(user: Annotated[dict, Depends(active_user)]):
return {"id": user["id"], "name": user["name"]}
@app.get("/admin")
def admin(user: Annotated[dict, Depends(admin_user)]):
return {"admin_id": user["id"]}保存为 main.py 并启动,使用 curl.exe -i http://127.0.0.1:8000/admin -H "Authorization: Bearer demo-token"。本章令牌仅为演示查表;第 36 篇替换为真正的 JWT 验证。
一棵依赖图如何执行
先解析最底层需要的参数,再按依赖关系求值,最后执行路由。任何一层抛出异常,依赖它的后续业务就不会执行。多个分支共享相同依赖时,一次请求通常只计算一次。
例如路由同时声明 current_user 与 admin_user,后者又通过 active_user 使用 current_user,默认通常可复用同一请求内的结果。不要把这种缓存当成跨请求的全局登录缓存。
yield 资源按依赖关系清理
如果用户服务依赖数据库 Session,Session 必须比使用它的依赖活得更久。清理顺序与依赖关系相配合。使用不同 scope 时,还要符合请求级依赖不能依赖更短函数级资源等生命周期限制。
错误语义与测试
认证失败通常是 401,并按 Bearer 方案提供 WWW-Authenticate;已确认身份但无操作权限通常是 403。本例把头解析简化,生产直接使用安全工具类更稳妥。
测试时可覆盖 current_user,分别返回启用、停用、无管理员角色的用户,从而验证下游授权规则。不要把所有层都覆盖掉后只测试路由返回固定字符串。
练习:增加 editor_user 分支,让管理员与编辑共享身份解析;打印依赖调用次数,确认共享和每次新请求的行为。