# 依赖项 { #dependencies } **FastAPI** 提供了简单直观但功能强大的**依赖注入**系统。 它被设计得非常易用,能让任何开发者都能轻松把其他组件与 **FastAPI** 集成。 ## 什么是「依赖注入」 { #what-is-dependency-injection } 在编程中,**「依赖注入」**指的是,你的代码(本文中为*路径操作函数*)声明其运行所需并要使用的东西:“依赖”。 然后,由该系统(本文中为 **FastAPI**)负责执行所有必要的逻辑,为你的代码提供这些所需的依赖(“注入”依赖)。 当你需要以下内容时,这非常有用: * 共享业务逻辑(同一段代码逻辑反复复用) * 共享数据库连接 * 实施安全、认证、角色权限等要求 * 以及更多其他内容... 同时尽量减少代码重复。 ## 第一步 { #first-steps } 先来看一个非常简单的例子。它现在简单到几乎没什么用。 但这样我们就可以专注于**依赖注入**系统是如何工作的。 ### 创建依赖项,或“dependable” { #create-a-dependency-or-dependable } 首先关注依赖项。 它只是一个函数,且可以接收与*路径操作函数*相同的所有参数: {* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *} 大功告成。 **2 行**。 它的形式和结构与所有*路径操作函数*相同。 你可以把它当作没有“装饰器”(没有 `@app.get("/some-path")`)的*路径操作函数*。 而且它可以返回任何你想要的内容。 本例中的依赖项预期接收: * 类型为 `str` 的可选查询参数 `q` * 类型为 `int` 的可选查询参数 `skip`,默认值 `0` * 类型为 `int` 的可选查询参数 `limit`,默认值 `100` 然后它只需返回一个包含这些值的 `dict`。 /// info | 信息 FastAPI 在 0.95.0 版本中新增了对 `Annotated` 的支持(并开始推荐使用)。 如果你的版本较旧,尝试使用 `Annotated` 会报错。 在使用 `Annotated` 之前,请确保[升级 FastAPI 版本](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank}到至少 0.95.1。 /// ### 导入 `Depends` { #import-depends } {* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *} ### 在“dependant”中声明依赖项 { #declare-the-dependency-in-the-dependant } 与在*路径操作函数*的参数中使用 `Body`、`Query` 等相同,给参数使用 `Depends` 来声明一个新的依赖项: {* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *} 虽然你在函数参数中使用 `Depends` 的方式与 `Body`、`Query` 等相同,但 `Depends` 的工作方式略有不同。 这里只能给 `Depends` 传入一个参数。 这个参数必须是类似函数的可调用对象。 你不需要直接调用它(不要在末尾加括号),只需将其作为参数传给 `Depends()`。 该函数接收的参数与*路径操作函数*的参数相同。 /// tip | 提示 下一章会介绍除了函数之外,还有哪些“东西”可以用作依赖项。 /// 接收到新的请求时,**FastAPI** 会负责: * 用正确的参数调用你的依赖项(“dependable”)函数 * 获取函数返回的结果 * 将该结果赋值给你的*路径操作函数*中的参数 ```mermaid graph TB common_parameters(["common_parameters"]) read_items["/items/"] read_users["/users/"] common_parameters --> read_items common_parameters --> read_users ``` 这样,你只需编写一次共享代码,**FastAPI** 会在你的*路径操作*中为你调用它。 /// check | 检查 注意,无需创建专门的类并传给 **FastAPI** 去“注册”之类的操作。 只要把它传给 `Depends`,**FastAPI** 就知道该怎么做了。 /// ## 共享 `Annotated` 依赖项 { #share-annotated-dependencies } 在上面的示例中,你会发现这里有一点点**代码重复**。 当你需要使用 `common_parameters()` 这个依赖时,你必须写出完整的带类型注解和 `Depends()` 的参数: ```Python commons: Annotated[dict, Depends(common_parameters)] ``` 但因为我们使用了 `Annotated`,可以把这个 `Annotated` 的值存到一个变量里,在多个地方复用: {* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *} /// tip | 提示 这只是标准的 Python,叫做“类型别名”,并不是 **FastAPI** 特有的。 但因为 **FastAPI** 基于 Python 标准(包括 `Annotated`),你就可以在代码里使用这个技巧。😎 /// 这些依赖会照常工作,而**最棒的是**,**类型信息会被保留**,这意味着你的编辑器依然能提供**自动补全**、**行内报错**等。同样适用于 `mypy` 等其他工具。 当你在**大型代码库**中,在**很多*路径操作***里反复使用**相同的依赖**时,这会特别有用。 ## 要不要使用 `async`? { #to-async-or-not-to-async } 由于依赖项也会由 **FastAPI** 调用(与*路径操作函数*相同),因此定义函数时同样的规则也适用。 你可以使用 `async def` 或普通的 `def`。 你可以在普通的 `def` *路径操作函数*中声明 `async def` 的依赖项;也可以在异步的 `async def` *路径操作函数*中声明普通的 `def` 依赖项,等等。 都没关系,**FastAPI** 知道该怎么处理。 /// note | 注意 如果不了解异步,请参阅文档中关于 `async` 和 `await` 的章节:[异步:*“着急了?”*](../../async.md#in-a-hurry){.internal-link target=_blank}。 /// ## 与 OpenAPI 集成 { #integrated-with-openapi } 依赖项及子依赖项中声明的所有请求、验证和需求都会集成到同一个 OpenAPI 模式中。 因此,交互式文档中也会包含这些依赖项的所有信息: ## 简单用法 { #simple-usage } 观察一下就会发现,只要*路径*和*操作*匹配,就会使用声明的*路径操作函数*。随后,**FastAPI** 会用正确的参数调用该函数,并从请求中提取数据。 事实上,所有(或大多数)Web 框架的工作方式都是这样的。 你从不会直接调用这些函数。它们由你的框架(此处为 **FastAPI**)调用。 通过依赖注入系统,你还可以告诉 **FastAPI**,你的*路径操作函数*还“依赖”某些应在*路径操作函数*之前执行的内容,**FastAPI** 会负责执行它并“注入”结果。 “依赖注入”的其他常见术语包括: * 资源(resources) * 提供方(providers) * 服务(services) * 可注入(injectables) * 组件(components) ## **FastAPI** 插件 { #fastapi-plug-ins } 可以使用**依赖注入**系统构建集成和“插件”。但实际上,根本**不需要创建“插件”**,因为通过依赖项可以声明无限多的集成与交互,使其可用于*路径操作函数*。 依赖项可以用非常简单直观的方式创建,你只需导入所需的 Python 包,用*字面意义上的*几行代码就能把它们与你的 API 函数集成起来。 在接下来的章节中,你会看到关于关系型数据库、NoSQL 数据库、安全等方面的示例。 ## **FastAPI** 兼容性 { #fastapi-compatibility } 依赖注入系统的简洁让 **FastAPI** 能与以下内容兼容: * 各类关系型数据库 * NoSQL 数据库 * 外部包 * 外部 API * 认证与授权系统 * API 使用监控系统 * 响应数据注入系统 * 等等... ## 简单而强大 { #simple-and-powerful } 虽然**层级式依赖注入系统**的定义与使用非常简单,但它依然非常强大。 你可以定义依赖其他依赖项的依赖项。 最终会构建出一个依赖项的层级树,**依赖注入**系统会处理所有这些依赖(及其子依赖),并在每一步提供(注入)相应的结果。 例如,假设你有 4 个 API 路径操作(*端点*): * `/items/public/` * `/items/private/` * `/users/{user_id}/activate` * `/items/pro/` 你可以仅通过依赖项及其子依赖项为它们添加不同的权限要求: ```mermaid 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 ``` ## 与 **OpenAPI** 集成 { #integrated-with-openapi_1 } 在声明需求的同时,所有这些依赖项也会为你的*路径操作*添加参数、验证等内容。 **FastAPI** 会负责把这些全部添加到 OpenAPI 模式中,以便它们显示在交互式文档系统里。