依赖注入基础 Depends
本教程共 50 篇 · 第 22 篇 · 更新于 2026-08-12 · 约 8 分钟阅读
本节目标:搞懂什么是依赖注入,学会用
Depends()把重复的逻辑抽成一个函数,让多个接口自动复用它。
写接口时你会发现,很多路径操作都要处理同一批参数。比如分页用的 skip、limit,或者鉴权时要解析的 token。如果每条路径操作都写一遍,代码又臭又长。
FastAPI 给了一个很省事的办法,叫依赖注入(Dependency Injection)。它让你把共用逻辑写成一个普通函数,然后在路径操作里”挂”上去,框架会自动帮你调用、把结果送进来。
22-1 什么是依赖注入
依赖注入听起来很拗口,其实意思很简单:你的路径操作函数先”声明”自己需要什么,比如”我需要一个解析好的查询参数集合”。然后由 FastAPI 负责把这件东西准备好,再”注入”给你的函数。
你只管声明需要,不亲自去创建它。这件事在日常里到处都是,比如:
- 多个接口共用一套参数校验逻辑
- 多个接口共享同一个数据库连接
- 统一做登录校验、权限检查
这些场景的共同点,就是”代码会被重复写好几遍”。依赖注入帮你把重复消灭掉。
22-2 写一个最简单的依赖
依赖本身就是一个函数,它长得和路径操作函数一模一样,只不过没有 @app.get() 这种装饰器。它可以接收查询参数、路径参数、请求体等等。
下面这个 common_parameters 就是个依赖。它接收三个查询参数,打包成一个字典返回:
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,
) -> dict:
return {"q": q, "skip": skip, "limit": limit}
注意,这里没有用老式的 Optional[str],而是用了 Python 3.10 的 str | None 写法。函数体就两行,返回一个字典。
你可以把它看成”一个没有装饰器的路径操作函数”。它想返回什么就返回什么,这里返回字典,后面也能返回对象、列表、连接等等。
22-3 用 Depends 挂上依赖
要使用这个依赖,得在路径操作函数的参数里写 Depends(你的函数),而不是直接调用它。看代码:
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,
) -> dict:
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(
commons: Annotated[dict, Depends(common_parameters)],
) -> dict:
return commons
@app.get("/users/")
async def read_users(
commons: Annotated[dict, Depends(common_parameters)],
) -> dict:
return commons
这里有个关键细节:Depends(common_parameters) 只传了函数本身,没有加括号去调用它。FastAPI 会替你调用。
当有请求进来时,FastAPI 会做三件事:
- 用正确的参数去调用
common_parameters - 拿到它返回的结果
- 把结果赋值给路径操作函数的
commons参数
所以你的 read_items 拿到的 commons,就是依赖返回的字典。你完全不用自己写调用代码。
Tip你不需要专门建一个类、向 FastAPI “注册”什么。只要把函数传给
Depends,框架就知道怎么处理它。这正是依赖注入好上手的地方。
22-4 一个依赖,多处复用
依赖最大的价值就是复用。上面两个接口 /items/ 和 /users/ 都用了 common_parameters。参数声明、类型转换、校验,全部只写了一遍。
假如以后要给所有接口加一个通用的查询参数,你只改 common_parameters 一个地方,所有挂着它的路径操作立刻生效。这就是”减少重复代码”的真实含义。
而且依赖可以声明任意参数,跟路径操作函数完全一致。依赖里也能接收 Query、Header、Cookie,甚至别的依赖。它本质上是同一套机制。
22-5 依赖函数用 async 还是 def
依赖函数和路径操作函数一样,既可以是 async def,也可以是普通 def。FastAPI 都能正确处理。
一般来说,只要你的逻辑里涉及 await、要等待数据库或网络,就用 async def。如果依赖里做的是纯计算、纯校验,没有等待,用普通 def 也行。两者还能混着用:同步路径操作里挂异步依赖,异步路径操作里挂同步依赖,FastAPI 都不会报错。
本教程默认推荐 async def,除非你在讲”阻塞 I/O”才用 def。
22-6 文档里自动展示依赖
依赖声明的所有参数、校验规则,都会自动合并进同一个 OpenAPI 描述里。也就是说,你在 /docs 交互式文档中,能看到依赖带来的那些查询参数。
比如访问 /items/,文档里会显示 q、skip、limit 三个参数。它们其实来自依赖,而不是写在路径操作函数里。FastAPI 帮你把它们”向上合并”了。
这对前端同学特别友好:他们看一个接口的文档,就能知道要传哪些参数,不用去翻依赖的实现。
22-7 用 Annotated 别名少写重复
如果你在好几个路径操作里都要用 common_parameters,每次都写 commons: Annotated[dict, Depends(common_parameters)] 有点啰嗦。可以把它存成一个变量复用:
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,
) -> dict:
return {"q": q, "skip": skip, "limit": limit}
CommonsDep = Annotated[dict, Depends(common_parameters)]
@app.get("/items/")
async def read_items(commons: CommonsDep) -> dict:
return commons
@app.get("/users/")
async def read_users(commons: CommonsDep) -> dict:
return commons
这只是标准 Python 的类型别名技巧,不是 FastAPI 特有的。好处是类型信息被保留,编辑器依然能给你补全和报错提示。在大型项目里反复用同一个依赖时,这招很实用。
22-8 依赖能做什么:鉴权与数据库思路
依赖注入不只是省几行参数声明。它在 FastAPI 里是统一做”预处理”的武器。
比如登录校验,你可以写一个依赖,从请求头里取出 token、解析出当前用户。哪个接口需要登录,就在参数里挂上这个依赖。没登录的依赖直接抛异常,接口根本不会执行。
再比如数据库,你可以写一个依赖负责创建数据库连接、用完再关掉(第 25 章会讲怎么优雅地关)。每个需要查库的接口挂上它,就能拿到一个现成的连接。
这些思路背后都是同一套机制:把”准备资源”的逻辑交给依赖,路径操作函数只管干活。
22-9 小结
依赖注入就是让路径操作函数”声明需要、框架注入”。你写一个普通函数作为依赖,用 Depends() 挂在路径操作的参数上,FastAPI 自动调用并把结果送进来。
一个依赖可以在多个接口复用,减少重复;它的参数会进 OpenAPI、出现在文档里;用 Annotated 还能少写重复代码。下一章我们看,除了函数,类也能当依赖,还能层层嵌套。