首页
/ FastAPI 全局依赖(Global Dependencies):用 FastAPI(dependencies=...) 为整应用统一挂载公共依赖

FastAPI 全局依赖(Global Dependencies):用 FastAPI(dependencies=...) 为整应用统一挂载公共依赖

2026-09-06 11:58:34作者:庞队千Virginia

在 FastAPI 中,依赖(Dependency)不仅可以挂在单个端点或路由上,还可以通过 FastAPI() 构造器的 dependencies 参数声明为全局依赖,使其自动作用于应用中的每一条路径操作(path operation),包括通过 include_router 挂载的子路由。读完本篇,你将掌握全局依赖的完整可运行示例、其生效范围与执行顺序、从源码层面验证“全局”二字的实现机制,以及它与 Router 级分组依赖、端点级 Depends 的取舍策略。

什么场景需要全局依赖

对于某些类型的应用(例如多租户 SaaS、需要统一鉴权的 API 网关),你希望每一次请求——无论访问哪个端点——都先经过相同的公共逻辑,比如:

  • 校验统一携带的 Token / API Key 请求头;
  • 解析租户(tenant)标识并写入上下文;
  • 审计日志、限流标记等横切逻辑。

与其在每个端点函数里重复写 Depends(...),不如把这些依赖声明一次、作用于整个应用。这与你给路径操作装饰器添加 dependencies 列表的思路完全一致(参见官方文档 在路径操作装饰器中添加 dependencies),区别只在于作用域从“单个操作”放大到了“整个应用”。

完整示例:应用级 Token 与 Key 双重校验

官方教程对应的示例源码是 docs_src/dependencies/tutorial012_an_py310.py(标准 Annotated 风格),完整代码如下:

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException


async def verify_token(x_token: Annotated[str, Header()]):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


async def verify_key(x_key: Annotated[str, Header()]):
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key


# 关键一行:把依赖声明在 FastAPI 应用上
app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])


@app.get("/items/")
async def read_items():
    return [{"item": "Portal Gun"}, {"item": "Plumbus"}]


@app.get("/users/")
async def read_users():
    return [{"username": "Rick"}, {"username": "Morty"}]

几个要点:

  1. 关键就是第 17 行 app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])dependencies 参数接受一个 Sequence[Depends],列表中每个元素都会应用到应用中的每条路径操作上。
  2. 端点函数 read_itemsread_users 本身没有任何依赖参数,但请求仍必须先通过 verify_tokenverify_key 两道校验。
  3. 注意 verify_key 虽然 return x_key,但全局依赖列表中的依赖不会把返回值注入端点函数参数——这与路由级 dependencies 列表的语义一致。如果你需要在端点里拿到某个依赖的返回值,应当改用函数参数中的 Depends(...)
  4. Header() 默认开启 convert_underscores,因此参数 x_token 对应请求头 X-Tokenx_key 对应 X-Key

仓库同时提供等价的旧式默认参数风格版本 docs_src/dependencies/tutorial012_py310.py

from fastapi import Depends, FastAPI, Header, HTTPException

async def verify_token(x_token: str = Header()):
    ...

async def verify_key(x_key: str = Header()):
    ...

app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])

运行与验证

uvicorn tutorial012_an_py310:app --reload

请求缺失请求头(预期返回 400):

curl -i http://127.0.0.1:8000/items/
# HTTP/1.1 400 Bad Request
# {"detail":"X-Token header invalid"}

携带合法请求头(预期返回 200):

curl -i \
  -H "X-Token: fake-super-secret-token" \
  -H "X-Key: fake-super-secret-key" \
  http://127.0.0.1:8000/items/
# HTTP/1.1 200 OK
# [{"item":"Portal Gun"},{"item":"Plumbus"}]

/users/ 端点表现完全相同——这正是“全局”的含义。

源码剖析:全局依赖如何落到每一条路由上

1. FastAPI 构造器:参数定义与传递

fastapi/applications.py 中,FastAPI.__init__dependencies 参数有如下类型与文档:

dependencies: Annotated[
    Sequence[Depends] | None,
    Doc(
        """
        A list of global dependencies, they will be applied to each
        *path operation*, including in sub-routers.
        ...
        """
    ),
] = None,

文档明确写着“applied to each path operation, including in sub-routers”。随后在构造函数内(fastapi/applications.py),该列表被原样传给应用的主路由:

self.router: routing.APIRouter = routing.APIRouter(
    routes=routes,
    ...
    dependencies=dependencies,
    ...
)

也就是说,应用级全局依赖本质上就是主 APIRouter 上的路由级依赖

2. 子路由继承:为什么 include_router 进来的端点也会被覆盖

“全局”的关键在于路由的合并逻辑。在 fastapi/routing.pyAPIRouter.include_router 中:

dependencies=[*parent_router.dependencies, *(dependencies or [])],

父路由(即主 app.router,携带着全局依赖列表)的 dependencies 会被前置合并进每一个被 include 的子路由上下文;随后注册具体路由时(fastapi/routing.py):

dependencies=[*self.include_context.dependencies, *route.dependencies],

每条 APIRoute 最终拿到的依赖列表 = 主路由(全局)依赖 + 子路由自身依赖 + 装饰器上的 dependencies。这就是为什么即使端点定义在 APIRouter(prefix="/v1") 里再 app.include_router(...),全局依赖依然生效。

3. 依赖如何被求解:_build_dependant_with_parameterless_dependencies

合并后的列表最终在 fastapi/routing.py 被转换进路由的依赖图:

def _build_dependant_with_parameterless_dependencies(
    *,
    dependant: params.Dependant,
    dependencies: Sequence[params.Depends],
):
    for depends in dependencies[::-1]:
        dependant.dependencies.insert(
            0,
            get_dependant(path=dependant.path, call=depends.dependency),
        )
    return dependant

从源码结构看,这里以逆序遍历 + 逐个 insert(0) 的方式把列表依赖插入依赖图,最终保持了列表的声明顺序——即 dependencies=[Depends(verify_token), Depends(verify_key)] 会先执行 verify_token,它抛出 HTTPExceptionverify_key 就不会再被执行。每个请求处理时,主路由会调用 solve_dependencies(见 fastapi/routing.py)来真正求解依赖图并执行这些依赖函数。

4. 全局依赖同样支持 dependency_overrides

全局依赖在测试时可以像其他依赖一样被整体替换。FastAPI 实例上的 dependency_overrides 字典(fastapi/applications.py)用于“把昂贵的依赖替换成测试版本”:

from fastapi.testclient import TestClient
from docs_src.dependencies.tutorial012_an_py310 import app, verify_key, verify_token

def fake_token():
    pass

def fake_key():
    return "fake-key"

app.dependency_overrides[verify_token] = fake_token
app.dependency_overrides[verify_key] = fake_key

client = TestClient(app)
r = client.get("/items/")  # 无需真实请求头即可通过校验
print(r.json())

这说明测试全局约束(如鉴权)时,不需要真的伪造合法的 Token 请求头,直接覆写依赖即可。

三种作用域对比:FastAPI 级、Router 级、装饰器级

声明位置 生效范围 代码形态
FastAPI(dependencies=[...]) 整个应用的所有路径操作,含子路由 app = FastAPI(dependencies=[Depends(func_dep_1)])
APIRouter(dependencies=[...]) / app.include_router(..., dependencies=[...]) 该路由(组)下的所有路径操作 router = APIRouter(dependencies=[Depends(get_token_header)])
路径操作装饰器 @app.get("/x/", dependencies=[...]) 单条路径操作 @app.get("/generate", dependencies=[Depends(get_user)])
端点函数参数 单个端点(且返回值可注入) def f(user=Depends(get_user))

官方教程在讲解全局依赖时同时给出了分组依赖的预告:当你阅读关于如何组织大型应用(官方文档 Bigger Applications – Multiple Files 一节)时,会学到如何为一组路径操作声明单个 dependencies 参数——即在 APIRouter 构造器或 include_router 上传入 dependencies。在 fastapi/applications.py 中,include_routerdependencies 参数文档也给出了同样的示例:

app.include_router(
    users_router,
    prefix="/users",
    dependencies=[Depends(get_current_user)],
)

经验法则是:能收窄就收窄。只有“所有端点无一例外都需要”的逻辑(如统一鉴权头)才值得放进 FastAPI(dependencies=...);只有一部分端点需要的校验应放在 Router 级或端点参数上,避免无关端点承担多余的求解开销与耦合。

测试用例印证

官方测试套件对本文示例做了回归验证:tests/test_tutorial/test_dependencies/test_tutorial012.py 同时参数化覆盖了 tutorial012_py310tutorial012_an_py310 两种写法,确保“应用级 dependencies 对全部端点生效、缺失请求头返回 400”的行为在不同风格写法下保持一致。

小结

  • FastAPI(dependencies=[Depends(...), ...]) 是声明应用级全局依赖的唯一入口,官方文档对应页面为 docs/de/docs/tutorial/dependencies/global-dependencies.md
  • 从源码看,它被传入主 APIRouter,并在 include_router / 路由注册时通过 [*parent_router.dependencies, *route.dependencies] 逐级合并,保证子路由中的端点同样被覆盖;
  • 列表中依赖按声明顺序求解,前置依赖抛错即短路后续依赖;其返回值不注入端点参数;
  • 全局依赖可被 app.dependency_overrides 整体替换,便于测试;
  • 若只需要覆盖“一部分”端点,优先使用 APIRouter(dependencies=...) 或装饰器级 dependencies=[...],而不是应用级参数。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388