FastAPI 依赖注入实战指南用 Depends 抽离公共逻辑、共享连接与安全校验【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi依赖注入Dependency Injection是 FastAPI 最核心也最易上手的机制之一。本文以 官方韩语文档「Dependencies」 为骨架配合仓库内可运行示例 tutorial001_an_py310.py、tutorial001_02_an_py310.py 以及 Depends 的真实源码实现从零讲解如何用Depends()声明依赖、由 FastAPI 自动注入结果从而在一行代码内复用鉴权、数据库连接、分页参数等逻辑。读完本文你将掌握什么是可依赖项dependable、如何在路径操作函数中声明依赖、如何用Annotated类型别名批量复用依赖以及async def与普通def的混用规则。什么是依赖注入在编程中依赖注入指这样一种设计由你的代码这里指路径操作函数声明它运行所需的东西——也就是依赖随后由框架系统这里是FastAPI负责完成一切必要工作把这些依赖注入给你的代码。这在以下场景尤其有价值存在公共逻辑——同一段代码逻辑被反复书写需要共享数据库连接需要强制安全、认证、角色要求等约束以及许多其他场景……以上所有目标都能在最小化代码重复的前提下实现。第一步创建依赖可依赖项我们先看一个刻意保持简单的示例。它当下或许没什么用却能最清晰地展示依赖注入系统的工作方式。你可以把可依赖项dependable理解成一个没有装饰器的路径操作函数——没有app.get(/some-path)那种东西其余形态、结构完全一致而且可以返回任意内容。本仓库中的对应示例为 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的函数就是一个可依赖项。它期待三个可选查询参数参数类型默认值说明qstr \| NoneNone可选的查询参数skipint0可选的查询参数默认跳过 0 条limitint100可选的查询参数默认最多返回 100 条函数体仅做一件事把这三个值打包进一个dict返回。没错核心就这两行——依赖的创建并不需要继承任何基类或实现任何接口。版本提示使用Annotated上例使用Annotated承载Depends。FastAPI 自0.95.0起才支持并开始推荐Annotated写法若你使用更早版本直接使用Annotated会报错。因此在使用前请务必先将 FastAPI 升级到至少 0.95.1升级方法见仓库文档 docs/ko/docs/deployment/versions.md 中的升级 FastAPI 版本一节。导入Depends并在依赖方中声明依赖使用依赖前先导入Dependsfrom fastapi import Depends, FastAPI导入方式与示例文件第 3 行一致。随后就像用Body、Query等声明函数参数一样用Depends声明一个新的参数app.get(/items/) async def read_items(commons: Annotated[dict, Depends(common_parameters)]): return commons虽然Depends在书写位置上与Body、Query相同但它工作方式完全不同你只给Depends传一个参数这个参数必须是函数之类的可调用对象你不要自己调用它——末尾不要加括号只需把函数对象整体传给Depends()这个函数接收参数的方式与路径操作函数完全一致因此它也支持Query、Body、路径参数等一切 FastAPI 参数声明。每当有新请求到达时FastAPI会依次负责以正确的参数调用你的依赖可依赖项函数从函数取得返回值把该结果赋给路径操作函数中对应的参数。例如上面的common_parameters被/items/与/users/两个路径操作同时复用流程如下这样公共代码只需编写一次FastAPI 会替所有路径操作完成调用。提示你无需创建特殊类、也无需把它注册到 FastAPI 的任何地方——只要把它传给DependsFastAPI 便知道如何处理其余部分。源码视角Depends究竟是什么从仓库源码看Depends在 fastapi/params.py#L745-L749 中被定义为一个frozen dataclassdataclass(frozenTrue) class Depends: dependency: Callable[..., Any] | None None use_cache: bool True scope: Literal[function, request] | None None它本身几乎不携带逻辑只作为标记携带需要执行的依赖信息。真正对外暴露的工厂函数Depends()位于 fastapi/param_functions.py#L2283其关键行为包括dependency传入的可依赖项一个函数等可调用对象。文档反复强调不要直接调用它FastAPI 会在解析时自行按需调用use_cache默认True同一个请求内若同一依赖被多处声明例如被多个子依赖同时需要默认只会调用一次其结果在本次请求内被复用缓存设成False可禁用缓存、强制重复调用。这与子依赖章节中反复使用同一依赖的讨论相对应scope默认None主要服务于带yield的依赖用于界定依赖函数体的启止时机围绕路径操作函数还是围绕整个请求/响应周期。值得一提的是同一文件中紧随其后的 Security 类 直接继承自Depends并追加了scopes字段——可见安全相关依赖也是建立在本章机制之上的。用Annotated共享依赖类型别名细心的读者会发现上面每处使用common_parameters的地方都要完整书写类型注解与Depends()commons: Annotated[dict, Depends(common_parameters)]既然我们在使用Annotated完全可以把这段Annotated值存入一个变量然后在多处复用。见 docs_src/dependencies/tutorial001_02_an_py310.pyfrom 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要点说明这只是标准 Python特性名为type alias类型别名并非 FastAPI 专有因为 FastAPI 建立在 Python 标准之上包括Annotated你可以在自己的代码里放心使用这个技巧依赖依旧按预期工作而最大的好处是类型信息被完整保留——你的编辑器能持续提供自动补全、内联错误提示mypy等静态检查工具同样不受影响。这一招在大型代码库中价值尤其明显当同一个依赖在成百上千个路径操作里被反复使用时只需维护一处类型别名即可。async def还是普通def依赖同样由FastAPI调用与路径操作函数一致因此定义依赖时适用完全相同的规则既可以使用async def也可以使用普通def你可以在普通def的路径操作函数中声明async def依赖也可以在async def的路径操作函数中声明def依赖……任意组合都可以。没有关系FastAPI 知道该怎么做。如果你还不熟悉async/await的取舍可先阅读仓库文档 docs/ko/docs/async.md 中In a hurry?一节。补充说明从 fastapi/concurrency.py 等实现文件的存在可以推断FastAPI 会对普通def依赖在独立线程池中执行从而避免阻塞事件循环因此混用不会导致性能陷阱。与 OpenAPI 的无缝集成你所有依赖以及子依赖携带的请求声明、校验与要求都会被整合进同一个 OpenAPI 模式schema中。因此交互式文档会完整呈现这些依赖暴露的参数——比如common_parameters中的q、skip、limit会作为查询参数直接出现在/items/接口页面上。上图取自仓库 docs/en/docs/img/tutorial/dependencies/image01.png正是本文示例应用跑起来后127.0.0.1:8000/docs的真实界面除了响应区列出200 Successful Response与422 Validation Error外参数区的q、skip、limit均由依赖common_parameters自动注入 OpenAPI 模式而来。简单用法的本质框架替你调用函数换个角度看路径操作函数只是被声明为当路径与操作匹配时使用随后FastAPI负责以正确参数调用它并从请求中提取数据。事实上所有或绝大多数Web 框架都是这样工作的——你从不直接调用这些函数它们由框架调用。借助依赖注入系统你可以额外告诉 FastAPI路径操作函数还**依赖某段应在它之前执行的逻辑**FastAPI 会负责先执行并注入结果。对这一理念业界还有其他常见称呼资源resources提供者providers服务services可注入项injectables组件components插件与生态兼容依赖注入的开放性集成与插件完全可以用依赖注入系统构建而事实上根本无需创建插件——因为通过依赖你可以向路径操作函数声明无限多种集成与交互方式。创建依赖的方式非常直观只需import你需要的 Python 包再用寥寥几行代码把它与你的 API 函数接起来即可。后续文档中关于关系型 / NoSQL 数据库、安全等章节都建立在这一机制之上。正因依赖注入系统足够简单FastAPI 得以与以下对象天然兼容所有关系型数据库NoSQL 数据库外部 Python 包外部 API认证与授权系统API 使用监控系统响应数据注入系统等等简单而强大依赖树与子依赖分层hierarchical的依赖注入系统定义与使用都极其简单却依旧非常强大你可以定义自己还依赖其他依赖的依赖。最终会形成一棵分层的依赖树依赖注入系统负责为你求解整棵树包括各级子依赖并在每个层级提供注入结果。例如有 4 个 API 端点/items/public//items/private//users/{user_id}/activate/items/pro/你只需要依赖与子依赖即可为它们附加不同的权限要求图中呈现了一个典型的权限分层模型每个端点只声明它直接需要的依赖如active_user而active_user又依赖于current_useradmin_user、paying_user再进一步层层叠加。FastAPI 在求解时自底向上注入每一层结果你的路径操作函数无需关心校验过程发生在哪一层。同时所有这些依赖在声明各自要求的过程中也在为路径操作添加参数、校验等元数据FastAPI 会全部写入 OpenAPI 模式进而在交互式文档中如实展示。从源码与测试看实现的可靠性除了前面提到的Depends定义仓库为依赖注入准备了完整的实现与回归测试可作为你深入学习的入口依赖的解析、求解与注入逻辑集中在 fastapi/dependencies/ 目录含多个模块配合 fastapi/routing.py、fastapi/applications.py 共同完成请求链路的构建行为级测试覆盖了 缓存复用、ContextVar 隔离、依赖覆盖override、yield 型依赖的作用域 以及 依赖工具函数 等众多边界场景印证了本章所述同一请求内结果缓存等默认行为。下一步相关章节导览依赖注入的知识不止于此。韩语文档在同一目录下还有多个递进章节推荐按顺序研读子依赖Sub-dependencies——依赖再依赖其他依赖形成依赖树类作为依赖Classes as Dependencies——除函数外其他可当作依赖的东西路径操作装饰器中的依赖——不注入返回值、只执行附加逻辑的依赖全局依赖——对整组或整个应用生效的依赖带 yield 的依赖——用于资源释放与收尾逻辑。掌握本章的Depends与Annotated组合用法后你会发现数据库会话管理、当前用户解析、权限校验等横切关注点都能以极低的样板代码成本被干净地抽离并复用。【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考