首页
/ FastAPI 实战:在路径操作装饰器(Path Operation Decorator)中声明 Dependencies

FastAPI 实战:在路径操作装饰器(Path Operation Decorator)中声明 Dependencies

2026-09-07 23:15:05作者:滕妙奇

在 FastAPI 的依赖注入体系中,除了把 Depends 当作 path operation function 的参数使用,你还可以把一批只要求"被成功执行"、却不关心返回值的依赖声明在 path operation decorator(路由装饰器)的 dependencies 参数里,最常见的场景就是请求级别的鉴权 / 头校验。读完本文,你将掌握装饰器级依赖的声明语法、它在底层如何被求解执行、它与参数级依赖的差异,以及如何用仓库中的测试用例验证行为。

本文对应的原始文档为 dependencies-in-path-operation-decorators.md(英文原文见 docs/en/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md),配套示例代码位于 docs_src/dependencies/tutorial006_an_py310.py

为什么需要"只执行、不要返回值"的依赖

有些情况下,你在 path operation function 内部其实用不到某个依赖的返回值

  • 依赖本身不返回任何值(例如只做校验、鉴权、记录日志、计数);
  • 依赖虽然返回了值,但你并不需要在接口函数里使用它;
  • 你只是希望这个依赖必须被解析并执行(例如校验请求头、调用某个前置服务),执行失败就中止请求。

如果仍然把这些依赖写成 path operation function 的参数,就会得到一些"声明了却从未使用"的函数参数。针对这种情况,FastAPI 允许把 Depends()list 直接挂到 path operation decorator 上,而不是函数签名里。

在装饰器中添加 dependencies

path operation decorator(如 @app.get@app.post)接收一个可选参数 dependencies,它应当是 Depends() 组成的一个 list

仓库中的完整示例 docs_src/dependencies/tutorial006_an_py310.py(Python 3.10 + Annotated 写法):

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()


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


@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
    return [{"item": "Foo"}, {"item": "Bar"}]

要点解读:

  • 第 19 行把 dependencies=[Depends(verify_token), Depends(verify_key)] 传给了 @app.get
  • verify_tokenverify_key 会像普通依赖一样被解析、执行
  • 关键差异:即便它们返回了值(如 verify_key 返回了 x_key),这些值也不会被传入 read_items。注意 read_items() 的函数签名没有声明任何依赖参数,却依然能触发两个校验函数。

仓库还提供了不使用 Annotated 的等价写法 docs_src/dependencies/tutorial006_py310.py,使用默认值语法 x_token: str = Header(),其装饰器声明与业务逻辑完全一致,二者行为相同。

为什么要放在装饰器而非函数参数里

原文档给出了两条非常实际的动机:

  1. 规避编辑器 / 工具的 unused parameter 报错:部分编辑器会检查未使用的函数参数并将其标为错误。把这类"只执行、不消费"的依赖放进装饰器,既能保证它们被执行,又不会触发此类 tooling 提示;
  2. 避免误导新开发者:代码中出现一个从未被使用的参数,会让阅读者误以为它是多余的、可以被删掉。声明在装饰器中后,代码意图更清晰。

一个实用的头校验套路

上述示例通过 Header() 声明了自定义请求头 X-TokenX-Key 的校验,构成一个典型的"前置校验依赖"。需要说明的是:该示例中的头与校验逻辑是为教学发明的。原文档在 note 中明确提醒——在真实场景中实现安全(security)时,直接使用 FastAPI 集成的安全工具包收益更大,详见下一章 Security utilities(安全工具)

装饰器级依赖的错误与返回值行为

装饰器中的依赖其实就是你平时使用的那些同一个依赖函数,它们具备普通依赖的全部能力,可以细分为以下三点。

1. 声明请求要求(Dependency requirements)

装饰器级依赖可以声明请求要求(例如上面的请求头 Header()),也可以依赖其它子依赖(sub-dependencies)。示例中 verify_tokenx_tokenverify_keyx_key 均声明为必填请求头(见 tutorial006_an_py310.py 第 8、13 行),因此当请求缺少这些头时,请求在进入接口函数之前就会失败。

2. 抛出异常(Raise exceptions)

装饰器级依赖与普通依赖一样可以 raise 异常。示例中两处校验失败都会抛出 HTTPException(status_code=400, detail=...)(第 10、15 行)。由于依赖在 path operation function 调用前被求解,异常会直接中断请求流程并交由 FastAPI 的异常处理机制返回响应,接口函数体根本不会执行。

3. 返回值可用也可不用(Return values)

装饰器级依赖既可以返回值也可以不返回,返回值一律不会被使用。这意味着你可以复用一段已经在别处使用的、会返回值的普通依赖,即便返回值用不上,它依然会被可靠执行——示例中 verify_key 返回了 x_key(第 16 行),而接口函数完全用不到它,这正是"可复用的副作用式依赖"的经典用法。

结合三者可以看到:装饰器级依赖本质是"把需要执行的逻辑挂在路由上、但不把结果注入函数",因此非常适合认证、鉴权、限流这类"校验不通过就拒绝"的横切关注点。

底层原理:参数化装饰器依赖如何被构建与求解

在 FastAPI 的源码实现中,这一类依赖有一个专门的称呼:parameterless dependencies(无参数化依赖),其名称来自 Depends 参数 use_cache 之外的"无需把返回值注入函数"特性。下面从当前仓库的源码梳理它的完整调用链。

构建阶段:_build_dependant_with_parameterless_dependencies

路由在注册时会把装饰器上的 dependencies 转化为 Dependant 树中的一个个子依赖。核心实现位于 fastapi/routing.py_build_dependant_with_parameterless_dependencies(第 855~869 行):

def _build_dependant_with_parameterless_dependencies(
    *,
    path: str,
    call: Callable[..., Any],
    dependencies: Sequence[params.Depends],
) -> tuple[Dependant, list[ModelField], bool]:
    dependant = get_dependant(path=path, call=call, scope="function")
    for depends in dependencies[::-1]:
        dependant.dependencies.insert(
            0,
            get_parameterless_sub_dependant(depends=depends, path=path),
        )
    ...

注意两点实现细节:

  • 先由 get_dependant 依据路径操作函数本身构建主体 Dependant
  • 再对 dependencies 倒序迭代并逐个 insert(0, ...),最终保证它们在依赖列表中的顺序与声明顺序一致,即示例中 verify_token 会先于 verify_key 执行。

子依赖构建:get_parameterless_sub_dependant

每个 Depends(...) 会通过 fastapi/dependencies/utils.py 中的 get_parameterless_sub_dependant(第 132~144 行)转成独立的 Dependant。该函数断言 depends.dependency 必须是可调用对象,并委托 get_dependant 按被依赖函数的签名(比如其中的 Header 参数声明)递归展开其请求要求与更深层的子依赖。

执行阶段:solve_dependencies

真正进入请求处理时,FastAPI 会在调用 path operation function 之前调用 solve_dependencies(同样位于 fastapi/dependencies/utils.py,routing.py 中在 fastapi/routing.pyget_request_handler / app 请求流程中引用),对该 Dependant 树上所有依赖(包括装饰器注入的子依赖)逐层求解:解析参数、校验请求要求、执行依赖函数体。由此验证了原文档的说法——它们"与普通依赖的求解方式完全一致",只是结果不会被回传。

fastapi/applications.py 第 1719~1730 行对 dependencies 参数的 Doc 注释也可确认:每个路径操作装饰器都接收一个"由 Depends() 组成的、作用于该路径操作的依赖列表",其文档即指向本文主题。

用仓库测试用例验证执行顺序与失败语义

仓库为本文示例编写了完整的端到端测试:tests/test_tutorial/test_dependencies/test_tutorial006.py。该测试通过参数化 fixture 同时覆盖 tutorial006_py310tutorial006_an_py310 两个写法,验证的语义与原文档高度吻合:

  • 缺少请求头:不带任何头访问 /items/ 返回 422,错误体同时报告 x-tokenx-key 两个字段缺失(loc 均为 ["header", ...]),说明两个依赖的参数校验并行收集
  • 单个头非法:只带 X-Token 且值非法时返回 400{"detail": "X-Token header invalid"},证明 verify_tokenraise 且请求被中止;
  • 第二个头非法X-Token 正确、X-Key 非法时返回 400{"detail": "X-Key header invalid"},证明依赖按声明顺序执行、verify_key 同样生效;
  • 全部合法:携带两个正确头时返回 200[{"item": "Foo"}, {"item": "Bar"}],说明装饰器级依赖不会污染接口的入参与出参;
  • OpenAPI 文档:访问 /openapi.json 时,生成的参数表包含 x-tokenx-key 两个 required 请求头,说明装饰器级依赖的参数要求同样会进入 OpenAPI 模式。

如果你需要在本仓库中实际运行该测试,可执行:

pytest tests/test_tutorial/test_dependencies/test_tutorial006.py

扩展:为一组路径操作与整个应用声明依赖

装饰器级 dependencies 的粒度是单个路径操作。在原教程的后续章节中,这一参数会在更大的作用域上被复用:

  • 一组路径操作:当你把应用拆分为多文件结构(使用 APIRouter)后,可以在 include_router(..., dependencies=[...])APIRouter(dependencies=[...]) 处一次性为一批路径操作声明依赖,见 Bigger Applications - Multiple Files(大型应用:多文件)。源码层面可以在 fastapi/routing.pyAPIRouterinclude_router 相关实现(约第 2930~2945 行)看到其依赖列表会与子路由/路径操作依赖合并的线索;
  • 全局依赖:若希望依赖作用于应用中每一个路径操作,可在创建 FastAPI() 时传入 dependencies 参数,详细内容见教程下一篇 Global Dependencies(全局依赖)

从源码可以看出(例如 fastapi/routing.py 第 1441 行 dependencies=[*include_context.dependencies, *route.dependencies] 等合并逻辑),这三种作用域(应用级、路由分组级、路径操作级)的依赖在求解前会被合并进同一棵 Dependant 树,这正是"全局 → 分组 → 单条路由"逐层叠加的依赖架构。若想回顾装饰器级依赖与普通函数参数级依赖的完整区别,可回到本教程的入口 Dependencies(依赖注入)教程首页

小结

  • 当你需要某个依赖"被执行"、但用不到其返回值时,把 Depends() 列表放入 path operation decoratordependencies 参数,可让代码更干净并规避 unused parameter 提示;
  • 装饰器级依赖与普通依赖共享同一套解析、求解、异常机制:可以声明请求要求、可以 raise 异常、可以返回值但返回值不会注入接口函数;
  • 从源码看,此类依赖被建模为"parameterless dependencies",在 fastapi/routing.py_build_dependant_with_parameterless_dependencies 中被构建进 Dependant 树,并由 fastapi/dependencies/utils.pysolve_dependencies 统一求解;
  • 该能力适用于单条路由的鉴权 / 校验等前置逻辑;如需覆盖一组路径操作或整个应用,请进阶阅读 Bigger ApplicationsGlobal Dependencies;真正的生产级安全请优先采用 Security utilities
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
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