news 2026/9/7 3:04:26

FastAPI 进阶依赖指南:可参数化依赖(Callable 实例)与 `yield` 依赖生命周期演进详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FastAPI 进阶依赖指南:可参数化依赖(Callable 实例)与 `yield` 依赖生命周期演进详解

FastAPI 进阶依赖指南:可参数化依赖(Callable 实例)与yield依赖生命周期演进详解

【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi

本篇技术指南围绕 FastAPI 官方文档中的“进阶依赖”(Advanced Dependencies)主题展开,聚焦两大核心能力:一是通过callable(可调用)实例实现带参数的依赖,解决“同一套校验逻辑、不同固定参数”需要重复声明大量函数/类的问题;二是深入剖析yield的依赖scopeStreamingResponseexcept、后台任务等场景下的生命周期演进与迁移建议。读完你将掌握可参数化依赖的完整写法,理解Depends(scope="function")与默认scope="request"的差异,并能在升级旧版 FastAPI 应用时准确处理yield依赖的资源释放语义。

文档源代码示例位于仓库 docs_src/dependencies 目录,实现源码可对照 fastapi/params.py 与 fastapi/dependencies 深入阅读。

一、从“固定依赖”到“可参数化依赖”

在 FastAPI 的依赖注入体系中,此前教程中看到的依赖都是一个固定的函数或类——每次使用Depends()时,依赖本身的行为是确定的、写死的。

但在真实业务里,我们常常会遇到这样的需求:希望同一套校验/处理逻辑能接收不同的配置参数,从而避免为每一种参数组合声明大量几乎重复的函数或类。

原文档给出的是一个非常典型的场景:

我们希望有一个依赖,用于检查查询参数q中是否包含某段“固定内容”。同时,我们又希望这段“固定内容”是可以参数化的。

如果按“固定函数/固定类”的思路,每换一个固定内容(例如"bar""foo")就要新写一个函数或类,代码会迅速膨胀。而 Python 本身提供了一种优雅的机制来解决这个问题——类的可调用实例(callable instance)

二、可调用实例(Callable Instance)实现参数化依赖

2.1 什么是“可调用的实例”

Python 中,类本身天然是可调用的(调用它即实例化)。但这里要强调的是:让某个类的“实例”也变得可调用,而非类本身。

实现方式是在类中声明一个__call__方法。有了它,实例就能像函数一样被调用,例如checker(...)。原文档对应的示例代码位于 docs_src/dependencies/tutorial011_an_py310.py,其核心逻辑如下:

from typing import Annotated from fastapi import Depends, FastAPI app = FastAPI() class FixedContentQueryChecker: def __init__(self, fixed_content: str): self.fixed_content = fixed_content def __call__(self, q: str = ""): if q: return self.fixed_content in q return False checker = FixedContentQueryChecker("bar") @app.get("/query-checker/") async def read_query_check(fixed_content_included: Annotated[bool, Depends(checker)]): return {"fixed_content_in_query": fixed_content_included}

从 FastAPI 的角度看,这个__call__方法有两重作用:

  1. 用来检查额外的参数和子依赖——FastAPI 会像解析普通依赖函数一样解析__call__的签名(这里的q: str = "");
  2. 最终被调用来产出一个值——该返回值会作为依赖解析结果,注入到path operation function的对应参数中(此处的fixed_content_included)。

2.2 通过__init__参数化实例

关键技巧在于:__init__声明“实例级参数”来参数化依赖

def __init__(self, fixed_content: str): self.fixed_content = fixed_content

此时,__init__纯 Python 层面的普通构造逻辑——FastAPI 永远不会去触碰或关心__init__中的内容,它只由我们自己的代码在创建实例时直接调用。这正是“参数化”得以成立的分工:

  • __init__负责把配置(如fixed_content)注入实例,供你按场景定制;
  • __call__负责接收请求相关的参数(如q),交由 FastAPI 当作依赖函数解析执行。

2.3 创建实例并作为依赖使用

先手工创建带固定参数的实例:

checker = FixedContentQueryChecker("bar")

此时该依赖实例内部已携带"bar",保存在属性checker.fixed_content中。接下来,使用实例本身而不是类:

async def read_query_check(fixed_content_included: Annotated[bool, Depends(checker)]): return {"fixed_content_in_query": fixed_content_included}

注意这里写的是Depends(checker)而不是Depends(FixedContentQueryChecker)——依赖对象是实例checker,而不是类

FastAPI 在解析该依赖时,等价于执行:

checker(q="somequery")

q来自真实请求的查询参数,返回值(布尔值,表示固定内容是否被包含)会被注入到path operation function的参数fixed_content_included中。若请求形如GET /query-checker/?q=somequerybar,返回值即{"fixed_content_in_query": true}

2.4 真实世界同类实现:安全模块正是这样写的

原文档特别提示:这套写法看起来“刻意且复杂”,示例是为了展示原理而故意简化;在安全(Security)章节中,有一批工具函数/类正是以完全相同的方式实现。理解了 callable 实例依赖,就等于理解了这些安全工具底层的运作方式。

在 fastapi/security 源码中可找到大量证据。例如 fastapi/security/oauth2.py 中的OAuth2PasswordBearer.__call__

async def __call__(self, request: Request) -> str | None: authorization = request.headers.get("Authorization") if not authorization: if self.auto_error: raise self.make_not_authenticated_error() else: return None return authorization

在教程中,用法正是“先参数化创建实例,再作为依赖注入”:

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") async def read_items(token: Annotated[str, Depends(oauth2_scheme)]): ...

OAuth2PasswordBearer(tokenUrl="token")就是在用__init__参数化(配置 tokenUrl、auto_error 等),随后 FastAPI 调用其__call__完成请求期校验。同样的__call__模式还出现在 fastapi/security/http.py(如HTTPBearerHTTPBasic)与 fastapi/security/api_key.py(API Key Header/Query/Cookie)中,相关中文文档可查阅 docs/es/docs/tutorial/security 下的教程。若你还想查看其他可调用安全依赖如何被用作实例,可阅读 docs/es/docs/advanced/security/oauth2-scopes.md。

三、yield依赖:scopeHTTPExceptionexcept与后台任务的演进

3.1 这部分内容什么时候才用得上?

原文档开篇即给出明确警告:大多数应用并不需要这些技术细节。它们主要服务于两类人:

  • 0.121.0 之前的旧版 FastAPI 应用升级、正遭遇yield依赖相关问题的开发者;
  • 需要深刻理解依赖生命周期、以便排查资源释放时机的进阶用户。

yield的依赖在 FastAPI 各版本中经历了一系列演进,用于覆盖不同用例并修复缺陷。下面按文档顺序梳理这些“历史变更”——它们直接决定了今天yield依赖的退出代码(yield之后的清理逻辑)在何时执行。

3.2yield依赖与scope(0.121.0 起支持)

FastAPI0.121.0起,为带yield的依赖新增了Depends(scope="function")支持。Dependsscope字段在源码中定义为字面量类型(见 fastapi/params.py):

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

两种取值决定了依赖的“退出代码”(yield之后的部分)何时执行:

scope取值依赖启动时机退出代码执行时机
"function"(需要显式指定)处理该请求的path operation function执行前path operation function结束后、响应发回客户端之前立刻执行
"request"默认值同上,请求处理前响应已经发送给客户端之后再执行

示例(详见 docs_src/dependencies/tutorial008e_an_py310.py):

from typing import Annotated from fastapi import Depends, FastAPI app = FastAPI() def get_username(): try: yield "Rick" finally: print("Cleanup up before response is sent") @app.get("/users/me") def get_user_me(username: Annotated[str, Depends(get_username, scope="function")]): return username

关于scope还有一条子依赖约束需要留意:

  • 声明为scope="request"(默认)的依赖,其所有子依赖也必须都是"request"作用域
  • scope="function"的依赖,其子依赖既可以是"function"也可以是"request"

原因在于:任何依赖都需要能先于其子依赖执行退出代码,因为退出时它可能仍要用到子依赖提供的资源。

关于“提前退出(Early exit)与scope”更完整的时序图与说明,可继续阅读 docs/es/docs/tutorial/dependencies/dependencies-with-yield.md 教程中的 “Salida temprana yscope” 一节。

3.3yield依赖与StreamingResponse的技术细节(0.118.0 回退)

0.118.0 之前,带yield的依赖会在path operation function返回后、发送响应之前就执行退出代码。这样设计的初衷是:避免在等待响应“走完网络”期间不必要地持有资源。

但这个设计带来了副作用:如果你返回的是StreamingResponse,则依赖的退出代码可能已经提前执行完毕。举例来说,如果数据库会话放在yield依赖中,那么流式传输数据时该StreamingResponse无法再使用这个会话——因为会话已在yield后的退出代码里被关闭。

0.118.0 对该行为进行了回退:让yield之后的退出代码改为在响应发送完成之后执行。

原文档附注指出:这一回退后的行为与0.106.0 之前的旧行为非常相似,但针对若干边界情况做了改进与 bug 修复。

3.4 需要“提前退出”的特定用例:手动关闭会话

某些特定条件下,旧行为(发送响应前执行退出代码)反而更省资源。原文档给出一类典型场景:

代码在yield依赖中使用数据库会话仅用于校验用户path operation function中不再使用该会话;同时响应传输很慢(如一个缓慢输出数据、且不使用数据库的StreamingResponse)。

此时若按默认scope="request",数据库会话会被一直持有到响应完全发送完毕;但既然响应阶段根本用不到数据库,持有会话就属于浪费。

先看问题形态的示例(docs_src/dependencies/tutorial013_an_py310.py):

import time from typing import Annotated from fastapi import Depends, FastAPI, HTTPException from fastapi.responses import StreamingResponse from sqlmodel import Field, Session, SQLModel, create_engine engine = create_engine("postgresql+psycopg://postgres:postgres@localhost/db") class User(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) name: str app = FastAPI() def get_session(): with Session(engine) as session: yield session def get_user(user_id: int, session: Annotated[Session, Depends(get_session)]): user = session.get(User, user_id) if not user: raise HTTPException(status_code=403, detail="Not authorized") def generate_stream(query: str): for ch in query: yield ch time.sleep(0.1) @app.get("/generate", dependencies=[Depends(get_user)]) def generate(query: str): return StreamingResponse(content=generate_stream(query))

这里的资源释放链路是:get_user中抛出的HTTPException(status_code=403, detail="Not authorized")用于校验;get_sessionyield之后自动关闭Session的退出代码(示例第 19~21 行的with块结束逻辑),会等到慢速响应全部发送完之后才执行;而generate_stream()(第 30~38 行,逐字符输出并time.sleep(0.1)模拟慢速流)并不使用数据库会话。

如果使用 SQLModel(或 SQLAlchemy)且恰好命中这类场景,可在不再需要会话时手动显式关闭(对比 docs_src/dependencies/tutorial014_an_py310.py,其差别在于校验完成后立即调用session.close()):

def get_user(user_id: int, session: Annotated[Session, Depends(get_session)]): user = session.get(User, user_id) if not user: raise HTTPException(status_code=403, detail="Not authorized") session.close()

这样,会话会提前释放数据库连接,供其他请求复用。

需要强调的是:如果你的用例确实需要更通用的“从yield依赖中提前退出”机制,原文档建议在 FastAPI 的 Discussion 中提出具体用例。若存在足够有说服力的早期关闭(early closing)需求,上游会考虑新增一种“选择加入早期关闭”的新方式——也就是说,目前并没有一个通用开关来开启这一旧行为,手动关闭资源是文档给出的落地路径。

3.5yield依赖与except的技术细节(0.110.0 变更)

0.110.0 之前,如果在带yield的依赖中通过except捕获了异常,却没有再次 raise,该异常仍会被自动抛出/转发到任何异常处理器(exception handlers)或内部服务器错误处理器。

0.110.0修改了这一行为,目的有两个:

  1. 修复“被转发的异常若没有对应处理器(即内部服务器错误)会导致未受控的内存消耗”的问题;
  2. 使其与普通 Python 代码的惯例保持一致——捕获后不重新抛出,就应当视为已处理。

对升级用户而言,这意味着如果你在except块中捕获了异常且希望交给上层异常处理器统一处理,必须显式地重新抛出(raise

3.6 后台任务与yield依赖的技术细节(0.106.0 变更)

0.106.0 之前yield之后无法抛出异常,依赖退出代码在响应发送之后才执行,因此 异常处理器(自定义异常处理器)此时早已执行完毕。这样设计的初衷,主要是允许在后台任务中复用依赖yield出来的对象——因为退出代码会等后台任务结束后才执行。

0.106.0修改了该行为,意图同样是“不在等待响应传输期间持有资源”。

原文档附带的建议非常实用:

后台任务通常是一组相互独立的逻辑,应当单独处理、自带资源(例如它自己的数据库连接)。这样做通常会得到更干净的代码。

因此,如果旧代码依赖“yield对象可在后台任务中使用”这一行为,现在应当:

  1. 在后台任务函数内部自建资源(例如新建一个数据库会话),而不是复用yield依赖里的会话;
  2. 后台任务内部只使用不依赖yield依赖资源的数据
  3. 不要把数据库对象直接作为参数传给后台任务函数,而是传对象的 ID,在后台任务内部用新会话重新查出该对象再处理。

3.7 演进时间线与迁移自查

综合原文档,带yield依赖的关键行为变更可按版本归纳为一张速查表,便于升级排查:

版本节点变更内容影响
0.106.0yield之后可抛异常;退出代码执行时机调整为发送响应前/不再等后台任务后台任务需自带资源,传入对象 ID 而非对象
0.110.0except捕获后不重新抛出时,不再自动转发异常需显式raise才能交给异常处理器
0.118.0回退:退出代码改为响应发送完成后执行,修复StreamingResponse无法使用已关闭会话的问题慢速流/长连接场景注意资源持有时间
0.121.0新增Depends(scope="function")支持可用"function"作用域获得“路径函数结束即清理”的精确控制

若要验证这些行为在当前版本代码库中的具体表现,可以参考对应测试用例,例如 tests/test_dependency_yield_scope.py 中针对scope="function"/scope="request"的退出代码顺序断言,以及 tests/test_dependency_yield_scope_websockets.py 中 WebSocket 场景下的等价覆盖。

四、小结

本文围绕 FastAPI 进阶依赖的两条主线完成了闭环:

  • 可参数化依赖:通过__init__携带配置 +__call__交由 FastAPI 解析执行,从而以“类的一个实例”作为Depends的依赖对象。这一模式不仅是定制校验逻辑的利器,更是 fastapi/security 中OAuth2PasswordBearerHTTPBearer、API Key 等安全依赖的底层实现方式。
  • yield依赖生命周期:从 0.106.0、0.110.0、0.118.0 到 0.121.0 的多次演进,核心矛盾始终是“尽早释放资源”与“确保响应/后台任务仍能使用被释放资源”之间的平衡;当前推荐的落地方案是:默认使用scope="request",需要提前清理时用Depends(scope="function")或在依赖内部显式close()资源,并在后台任务中自建资源、只传 ID。

理解这两条主线,就能在编写高阶依赖注入逻辑时拥有清晰的心智模型,也能在升级旧版 FastAPI 应用时快速定位由依赖退出时机变化引发的问题。

【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 3:04:01

华为eNSP实战:配置静态路由实现不同VLAN间通信

先调整好心态再动手:很多朋友刚学网络时,都会碰到一个问题——同一个交换机下的电脑,明明连在同一个设备上,但 A 电脑就是 ping 不通 B 电脑。查了一圈发现两台电脑的 IP 地址不在同一个网段,VLAN 也对不上&#xff0c…

作者头像 李华
网站建设 2026/9/7 3:03:31

2027免费论文AI检测网站额度速度与报告准确度对比

2027免费论文AI检测网站额度速度与报告准确度对比 在毕业论文初稿自查与中期修改阶段,频繁检测 AI 率带来的费用开销让不少研究生望而却步:2027免费论文AI检测网站额度速度与报告准确度对比究竟哪家更值得信赖?很多同学在网上随便搜索免费检…

作者头像 李华
网站建设 2026/9/7 3:03:30

基于LSTM的交通客流预测完整实践方案

简介:这是一份基于LSTM的轨道交通客流预测完整项目包,面向数据科学、交通数据分析初学者及课程设计人群,解决地铁客流时间序列建模与预测问题。压缩包共17个文件,包含5份客流与天气的表格数据、1份预测脚本、2份LSTM模型权重文件&…

作者头像 李华
网站建设 2026/9/7 3:00:43

从被动接诉到主动预警:校园网AI客服与投诉文本分析实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华