FastAPI 里的 deps.py,到底是不是 Spring 拦截器?
FastAPI 里的 deps.py,到底是不是 Spring 拦截器? 看见 workflows.py、ai.py、assets.py、tasks.py 都连到 deps.py,我第一反应是:这不就是 Spring 里的拦截器吗?答案是:认证效果有点像,但运行机制不是一回事。 我最近在看一个 Python 后端的知识图。 图里有个很显眼的现象: 几个 AP

本文目录
看见 workflows.py、ai.py、assets.py、tasks.py 都连到 deps.py,我第一反应是:这不就是 Spring 里的拦截器吗?答案是:认证效果有点像,但运行机制不是一回事。
我最近在看一个 Python 后端的知识图。
图里有个很显眼的现象:
workflows.py
ai.py
assets.py
tasks.py
\
deps.py几个 API 文件都在导入同一个 deps.py。
右侧还列出了几个函数:
get_token_verifier
get_db
get_current_identity
get_current_subject
get_current_access_token如果平时主要写 Spring,这个画面很容易触发一个熟悉的判断:
这是不是类似拦截器?请求先进来,检查 Token,再把用户信息交给 Controller?
这个类比不能说完全错。
但如果直接把 deps.py 翻译成“FastAPI 拦截器”,后面理解数据库会话、参数注入和资源清理时就会越来越别扭。
更准确的说法是:
deps.py 是普通 Python 模块;它集中提供依赖函数。API 路由通过 Depends(...) 声明需要什么,FastAPI 再负责调用依赖、注入结果和管理 yield 依赖的退出。
这篇就从这句话展开。
#01 deps.py 没有特殊魔法
先拆掉第一个误解。
deps.py 不是 FastAPI 的保留文件名,也不是放进去就会自动生效的目录。
它和下面这些名字没有本质区别:
dependencies.py
common.py
auth.py
context.py都只是普通 Python 模块。
项目喜欢叫它 deps.py,只是因为 deps 是 dependencies 的缩写。
里面通常集中放一组会被 API 重复使用的 callable:
async def get_current_identity():
...
async def get_current_subject():
...
async def get_db():
...
async def get_current_access_token():
...FastAPI 不会因为这些函数写在 deps.py 中,就自动扫描并执行它们。
真正让函数进入请求流程的是路由中的 Depends(...)。
#02 API 不是直接调用,而是在声明依赖
普通 Python 调用可能是:
identity = await get_current_identity()FastAPI 路由通常这样写:
from fastapi import Depends
@router.get("/tasks")
async def list_tasks(
identity = Depends(get_current_identity),
db = Depends(get_db),
):
...这段代码不是在定义函数时立刻执行 get_current_identity 和 get_db。
它是在告诉 FastAPI:
调用 list_tasks 之前
我需要 identity
我还需要 dbFastAPI 官方对依赖注入的定义也很直接:代码声明自己工作所需要的东西,由系统负责提供这些依赖。
于是一次请求的核心过程变成:
请求进入
→ FastAPI 读取 Depends 声明
→ 构建并解析依赖图
→ 调用依赖函数
→ 把结果注入路由参数
→ 依赖成功后调用路由函数“依赖图”三个字很重要。
它不一定是从上到下执行的一条固定流水线。
路由可以直接依赖多个函数;依赖函数也能继续声明自己的子依赖。FastAPI 会递归解析整张图,还会在一次请求中默认复用相同依赖的结果。
所以 Depends 更像一句声明:
我需要这个能力,具体怎么准备交给框架。
#03 Token 校验为什么看起来像拦截器
假设某个私有 API 需要当前身份:
@router.get("/private")
async def private_api(
identity = Depends(get_current_identity),
):
...get_current_identity 读取 Bearer Token,交给 Gateway 或认证服务校验。
Token 无效时,依赖可以主动抛出:
raise HTTPException(
status_code=401,
detail="Invalid authentication credentials",
)FastAPI 会停止后续调用,路由处理函数不会执行。
从外部效果看,这确实很像 Spring 里的认证过滤器或拦截器:
先检查身份
→ 失败直接拒绝
→ 成功才进入业务代码但这里要注意两个细节。
第一,Depends 本身不会自动校验 Token。
校验规则和 401 都是依赖函数自己定义的。函数如果只是 return 401,那只是返回了一个普通整数,并不会自动终止请求。
第二,认证失败不代表其他依赖一定从未执行。
如果认证依赖本身还依赖数据库、Token 提取器或其他子依赖,那么其中一些步骤可能已经发生。401 能保证的是:路由业务函数不再调用;已经进入生命周期的资源仍要正确清理。
#04 get_db 为什么不只是“拿一个数据库对象”
get_db 常见的写法是:
async def get_db():
async with async_session() as session:
yield session或者显式写成:
async def get_db():
session = async_session()
try:
yield session
finally:
await session.close()yield 前面负责准备资源:
创建数据库会话yield session 把会话交给路由函数:
async def list_assets(
db = Depends(get_db),
):
...响应完成或发生异常时,FastAPI 会退出已经进入的 yield 依赖,继续执行 yield 后面的代码或 finally 块。
但“FastAPI 自动关闭数据库”这句话不够严谨。
框架负责的是:
在正确的生命周期阶段恢复并退出依赖真正的关闭、回滚或释放动作,仍然必须由 get_db 自己定义。
如果写成:
async def get_db():
return async_session()FastAPI 不会凭参数名叫 db,就猜到应该执行 close()。
所以更准确的职责分工是:
FastAPI 负责触发退出;依赖函数负责定义如何清理。
另外,退出时机还会受依赖 scope 和响应类型影响。尤其是流式响应,不应该把它死记成“路由 return 的下一行立刻关闭”。
#05 你的理解对在哪里,还差哪一步
我们可以把前面的理解整理成一句话:
deps.py是一个导出依赖函数的普通模块;API 层按需导入,需要认证就声明认证依赖,需要数据库就声明数据库依赖。
这句话整体正确。
只需要补上两个限定。
#不是“导入了就执行”
仅仅写:
from .deps import get_db不会触发依赖注入。
还要通过 Depends(...) 声明:
db = Depends(get_db)#不是“API 自己调用和关闭”
通常不是路由手动写:
db = await get_db()
await db.close()而是 FastAPI 解析依赖、注入 db,并驱动 yield 依赖退出;具体关闭动作由依赖函数中的 finally 或上下文管理器定义。
完整版本可以写成:
deps.py 是普通 Python 模块,集中定义认证、用户身份、数据库会话等依赖 callable。API 路由导入这些 callable,并通过 Depends(...) 声明需求。FastAPI 在调用路由前解析依赖图、执行依赖并注入结果;对已经进入的 yield 依赖,在响应生命周期结束或异常时触发退出清理。
#06 它与 Spring 到底怎么对应
答案不是一个类名,而是一组机制。
#认证提前拒绝
FastAPI 的认证依赖:
Depends(get_current_identity)在效果上接近 Spring Security Filter Chain:身份失败时,业务 Controller 或路由函数不执行。
而且 Spring 官方明确提醒:MVC HandlerInterceptor 并不适合作为安全层,安全逻辑应优先使用 Spring Security,或者尽早集成到 Servlet Filter Chain。
所以如果讨论 Token 认证,最接近的首先是 Spring Security Filter,而不是普通 MVC Interceptor。
#注入当前用户
FastAPI:
identity = Depends(get_current_identity)更像 Spring Controller 中的:
@AuthenticationPrincipal UserPrincipal principal两边都是把已经准备好的认证主体注入方法参数。
#解析方法参数
FastAPI 会调用依赖 callable,把结果放进路由参数。
这一点又很像 Spring MVC 的 HandlerMethodArgumentResolver:它根据参数声明解析并提供 Controller 方法参数。
#管理请求级资源
get_db 的 yield + finally 负责请求期间的资源建立和释放。
这在职责上接近 Spring 对事务、持久化上下文和 EntityManager 生命周期的管理。
但 FastAPI 不会因为用了 yield 就自动拥有 Spring 的事务语义。提交、回滚和关闭仍然要在依赖或数据库框架中明确实现。
#路径前后处理
HandlerInterceptor 能在 Handler 前后执行逻辑。
FastAPI 依赖也能在路由之前运行,yield 后还能执行退出代码,所以这部分看起来相似。
但它不是通用请求拦截协议。如果目标是统一包裹整个请求、修改 Request/Response 或处理全局横切逻辑,FastAPI/Starlette 的 Middleware 往往更接近 Spring Filter。
#07 为什么项目喜欢把这些东西集中到 deps.py
因为 API 路由很容易重复这些基础逻辑:
解析 Authorization
验证 Token
获取当前用户 ID
创建数据库会话
向下游透传访问令牌如果每个路由都手写,认证策略和资源清理很快就会分叉。
集中到 deps.py 后,路由只声明业务真正需要的参数:
@router.post("/workflows")
async def create_workflow(
subject = Depends(get_current_subject),
db = Depends(get_db),
):
...它带来的不只是少写几行代码。
更重要的是:
- 认证失败行为保持一致;
- 当前用户的来源保持一致;
- 数据库会话的生命周期集中管理;
- 下游 Token 的获取方式集中管理;
- 测试时可以替换依赖;
- OpenAPI 文档能整合依赖产生的参数和安全要求。
这就是为什么知识图里会看到多个 API 文件共同指向 deps.py。
它是 API 层共用的基础设施入口,但不是自动扫描的全局拦截器。
#08 最后,用一句话记住
如果你来自 Spring,可以这样记:
从认证效果看,
deps.py + Depends 有点像拦截器;从机制看,它更像 Spring Security、方法参数解析和请求级资源管理的组合。
再精确一点:
deps.py:普通模块,提供依赖 callable
Depends:声明路由需要什么
FastAPI:解析、调用并注入依赖
yield/finally:定义资源退出和清理
Middleware:处理更全局的请求/响应横切逻辑所以,deps.py 不是“Python 版 Interceptor”。
它只是把 API 层反复需要的认证、身份和资源管理函数放在了一起。
真正让这套机制运转起来的,是 Depends(...) 声明和 FastAPI 的依赖解析系统。
#资料来源
- FastAPI Dependencies:https://fastapi.tiangolo.com/tutorial/dependencies/
- FastAPI Dependencies with yield:https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/
- Spring Framework MVC Interceptors:https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-config/interceptors.html
- Spring Security Servlet Architecture:https://docs.spring.io/spring-security/reference/servlet/architecture.html
- Spring MVC Method Arguments:https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-methods/arguments.html






评论
还没有评论,来说点什么吧。