Skip to content

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 分支,让管理员与编辑共享身份解析;打印依赖调用次数,确认共享和每次新请求的行为。