首页
/ FastAPI 依赖注入入门:用 `Depends` 构建共享逻辑与层级化依赖树

FastAPI 依赖注入入门:用 `Depends` 构建共享逻辑与层级化依赖树

2026-09-07 11:02:49作者:谭伦延

导读

FastAPI 内置了一套强大但直观的依赖注入(Dependency Injection)系统,它允许你的 path operation functions(路径操作函数)直接"声明"自己需要哪些东西(数据库连接、用户身份、通用查询参数……),剩下的一切由 FastAPI 在请求到达时自动完成:调用依赖、取得结果、把结果注入到函数参数。读完本篇,你将掌握定义依赖函数、通过 Depends 声明依赖、利用 Annotated 消除样板代码、混用 async def 与普通 def,以及理解"依赖可以再依赖其它依赖"从而构建层级化依赖树的全过程——这正是后续数据库、安全认证等所有集成功能的基石。

本文对应仓库中的入门章节 docs/es/docs/tutorial/dependencies/index.md(英文原版见 docs/en/docs/tutorial/dependencies/index.md),并结合仓库源码与示例代码做了源码级展开。

"依赖注入"到底指什么

依赖注入在编程中的含义是:你的代码(这里指 path operation functions)用一种方式声明自己运行所必需且会使用到的"依赖";随后由系统(这里指 FastAPI)负责完成其余工作,把依赖"注入"给你的代码。

也就是说,你不用自己去创建和获取那些东西,只要声明"我需要它",FastAPI 就会在恰当的时机调用依赖并传入结果。

它在真实项目里非常有用,典型场景包括:

  • 共享逻辑:同一段代码逻辑反复出现,抽成依赖后只写一次;
  • 共享数据库连接:多个接口复用同一个连接/会话;
  • 实施安全约束:身份认证、鉴权、角色校验、付费用户限制等;
  • 其它任意"在接口执行前必须准备好的东西"

所有这一切都能在最大限度减少代码重复的前提下完成。

第一步:创建一个"可依赖"函数

依赖的本质非常简单:它就是一个普通的 Python 函数,并且可以像 path operation function 一样声明路径参数、查询参数、请求体、Header、Cookie 等各类参数。

先看本仓库中最基础的示例,完整代码见 docs_src/dependencies/tutorial001_an_py310.py

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
    return commons


@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
    return commons

核心就是 common_parameters 这个函数——仅此而已,就这几行。它的外形和结构与普通的 path operation function 完全一致,可以把它理解成一个"没有装饰器(没有 @app.get(...))的路径操作函数"。

这个依赖声明了三个参数:

参数 类型 默认值 含义
q str | None None 可选的查询参数
skip int 0 分页起点
limit int 100 每页数量上限

依赖函数可以返回任意类型——这里只是把三个参数原样打包成一个 dict 返回。

仓库中还保留了不使用 Annotated 的等价写法 docs_src/dependencies/tutorial001_py310.py,参数形式为 commons: dict = Depends(common_parameters),效果一致。

版本注意Annotated 的支持(并成为推荐写法)始于 FastAPI 0.95.0。如果你使用更早的版本,使用 Annotated 会报错,请先将 FastAPI 升级到至少 0.95.1。

导入并声明 Depends

先从 fastapi 中导入 Depends

from fastapi import Depends, FastAPI

然后在 path operation function 的新参数上使用它,用法和 BodyQueryHeader 等一样:

@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
    return commons

虽然 Depends 在参数位置的书写方式与 BodyQuery 相同,但工作机制不同:

  • 它只接受一个参数:一个"可依赖的"(dependable)可调用对象,通常是函数;
  • 不要手动调用它:不要写 Depends(common_parameters()) 这种带括号的形式,直接把函数对象传给 Depends()
  • 被传入的函数,其参数声明方式与 path operation function 完全相同。

每一个新请求到达时,FastAPI 会自动完成三件事:

  1. 用正确的参数调用你的依赖函数;
  2. 拿到函数的返回值;
  3. 把该返回值赋值给 path operation function 中对应的参数。

于是代码中两个接口 /items//users/ 共享了同一条分页逻辑,其调用关系可以用下图表示:

graph TB

common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]

common_parameters --> read_items
common_parameters --> read_users

值得注意的是:你不需要创建任何特殊类,也不需要把它"注册"到 FastAPI 的某个地方。只需把它传给 Depends,FastAPI 自会处理好其余部分。

Annotated 共享同一依赖声明

上面的例子已经暴露出一小点代码重复:每次要用 common_parameters 依赖,都要完整写出带类型注解和 Depends() 的参数:

commons: Annotated[dict, Depends(common_parameters)]

由于使用了 Annotated,可以把这个 Annotated 值存入变量,再在多处复用。这正是仓库中 docs_src/dependencies/tutorial001_02_an_py310.py 演示的"别名(type alias)"技巧:

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


CommonsDep = Annotated[dict, Depends(common_parameters)]


@app.get("/items/")
async def read_items(commons: CommonsDep):
    return commons


@app.get("/users/")
async def read_users(commons: CommonsDep):
    return commons

Annotated[dict, Depends(common_parameters)] 命名为 CommonsDep 后,两个接口都只写一行 commons: CommonsDep

需要强调的是:这只是标准 Python 语法,并非 FastAPI 专属特性——它叫"类型别名"。正因为 FastAPI 建立在 Python 标准(包括 Annotated)之上,你才能直接使用这一技巧。依赖依旧按预期工作,并且最妙的是类型信息被完整保留:你的编辑器依然能提供自动补全、行内错误提示mypy 等静态检查工具也能正常工作。

当代码库足够大、同一个依赖被几十上百个 path operation 反复使用时,这一技巧消除的样板代码量非常可观。

async def 还是普通 def

依赖同样由 FastAPI 负责调用(与 path operation functions 一样),因此定义依赖时适用完全相同的规则:

  • 你可以写 async def,也可以写普通 def
  • async def 的依赖可以出现在普通 defpath operation function 里;
  • 普通 def 的依赖也可以出现在 async defpath operation function 里;
  • 混用没有关系——FastAPI 知道怎么处理(普通 def 会被放入线程池执行,async def 则在事件循环中执行)。

关于 async/await 基础概念,可参考仓库中的 Async 文档

自动集成到 OpenAPI 与交互式文档

所有在依赖(及其子依赖)里声明的请求参数、校验规则与需求,都会被合并进同一个 OpenAPI schema。因此 /docs 交互式文档中会自动出现来自依赖的参数信息。

下面的截图来自仓库的 image01.png,展示的就是上例中由 common_parameters 声明的 qskiplimit 三个查询参数如何出现在 Swagger UI 的 GET /items/ 接口面板里,包括参数类型、默认值以及 200/422 响应说明:

FastAPI /docs 交互文档中来自依赖注入的查询参数展示

这意味着:当你在依赖中声明参数时,文档、校验、类型转换一整套能力都"免费"获得,不需要任何额外配置。

简单背后的思想:一切皆由框架编排

不妨退一步看:path operation functions 本身也不是你手动调用的——只要某个 pathoperation 匹配,FastAPI 就会负责用正确的参数去调用它,并从请求中提取数据。实际上所有(或绝大多数)Web 框架都遵循这种模式。

依赖注入系统在此基础上更进一步:你可以告诉 FastAPI,"我的 path operation function依赖某个东西,它必须在我的函数执行前先运行",而 FastAPI 会负责执行它并把结果注入进来。

在其它技术社区中,同一概念常有不同叫法,本质都是一回事:

  • resources(资源)
  • providers(提供者)
  • services(服务)
  • injectables(可注入项)
  • components(组件)

"插件"?不需要——依赖注入本身即是扩展机制

很多框架依靠"插件"机制来扩展能力,而 FastAPI 中并没有必要专门去造插件:通过依赖注入,你可以声明无穷无尽的集成与交互,并让它们对 path operation functions 可用。

具体做法通常是:直接 import 你需要的第三方 Python 包,再用几行代码把它集成进 API 函数——仅此而已。后续章节(关系型 / NoSQL 数据库、安全认证等)会给出大量实例。

这种简单性正是 FastAPI 生态兼容面广的原因,依赖注入系统可以与以下一切协同工作:

  • 所有关系型数据库
  • NoSQL 数据库
  • 外部 Python 包
  • 外部 API
  • 认证与授权系统
  • API 用量监控系统
  • 响应数据注入系统
  • 等等……

简单却强大:依赖可以层层嵌套

虽然这套层级化的依赖注入系统定义和使用都非常简单,它依然非常强大。依赖本身可以再声明依赖:你定义的依赖 A 依赖 B,B 又依赖 C……最终形成一棵层级化依赖树(hierarchical dependency tree),由依赖注入系统替你递归地求解所有依赖(及其子依赖),并在每一层把结果注入到位。

例如,假定 API 有 4 个端点:

  • /items/public/
  • /items/private/
  • /users/{user_id}/activate
  • /items/pro/

仅凭依赖与子依赖的组合,就能为它们赋予互不相同的权限要求:

graph TB

current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])

public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]

current_user --> active_user
active_user --> admin_user
active_user --> paying_user

current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items

对应关系一目了然:

  • /items/public/:只要求 current_user(当前用户有效即可);
  • /items/private/:要求 active_user(激活用户),而 active_user 依赖 current_user
  • /users/{user_id}/activate:要求 admin_user(管理员),其先决条件是 active_user
  • /items/pro/:要求 paying_user(付费用户),同样先经过 active_user

所有这些依赖声明的参数、校验等需求,都会被 FastAPI 递归地加入每个 path operation,最终写进 OpenAPI schema,并在交互式文档中展示出来。

源码视角:Depends 与依赖求解是怎么实现的

理解背后的实现能让你的使用更加游刃有余。在 fastapi/params.py 中,Depends 是一个冻结数据类:

@dataclass(frozen=True)
class Depends:
    dependency: Callable[..., Any] | None = None
    use_cache: bool = True
    scope: Literal["function", "request"] | None = None

三个字段分别对应:

  • dependency:被注入的"可依赖"可调用对象(Depends 函数级文档见 fastapi/param_functions.py);
  • use_cache:默认 True。在同一请求中,若某个依赖被多处声明,第一次调用后的值会被缓存复用;设为 False 可禁用缓存、保证每次重新调用;
  • scope:主要服务于含 yield 的依赖(详见 dependencies-with-yield),"function" 表示依赖体包裹 path operation function 的执行区间,"request" 表示包裹整个请求—响应周期。

有趣的旁证是:安全相关的 Security 类正是 Depends 的子类(见 fastapi/params.py),所以安全机制与依赖注入在底层是同一套体系。

从执行链路看,每个路径操作在注册时会被建模成 fastapi/dependencies/models.py 中的 Dependant 对象(见 fastapi/dependencies/models.py),其 dependencies 字段递归挂载子依赖,从而形成上文提到的依赖树;请求到达后,fastapi/dependencies/utils.pysolve_dependencies 异步函数(见 fastapi/dependencies/utils.py)负责自底向上解析整棵树,并把每个依赖的返回值注入到下一层。仓库 tests/ 目录下也提供了丰富的回归测试,例如 test_dependencies_utils.pytest_dependency_cache.pytest_dependency_overrides.pytest_dependency_class.py,分别覆盖依赖缓存、测试覆盖替换、类作为依赖等行为,可作为深入研读的入口。

后续进阶路线

本篇是依赖注入的"第一卷"。在本仓库的 dependencies 教程目录 下,还可以继续学习:

掌握本文中的 Depends 声明方式、Annotated 别名复用与层级依赖树思想后,再去看安全认证、数据库会话管理等进阶主题,你会发现它们全部建立在这一套统一、简洁的机制之上。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

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