FastAPI 学习日记(6)— 依赖注入

内容

今天,我想写一下最近学习的 FastAPI 中的依赖注入(DI)。即使在之前的会话中(比如第三个),在接收 Pydantic 的 BaseModel 子类请求时,我也在无意识地使用 DI,但这次我决定更深入地研究 FastAPI 中的 DI。(从现在开始,我可能会简单地称依赖注入为 DI。)

FastAPI 是为 DI 设计的,或者说,是为了促进 DI 而设计的。

函数依赖注入

这是一种通过创建一个用于注入的函数,并通过它注入依赖项的方法。

准备了一个名为pagination的异步函数,如下所示,该函数设置为接受两个参数:offsetlimit。返回值是一个包含offsetlimit的元组。

在路径操作函数中,通过将pagination函数传递给Depends函数,存在一个机制,其中请求经过pagination函数,并注入offsetlimit的值。

在上一篇文章中,我已经写过如何使用Pydantic模型类接收请求数据,然而,仅凭这种方法可能并不十分有利,

通过这种方式进行操作,其中limit的值不能小于10,或者在这个注入函数中具有执行验证和值转换的自由,我发现这是这种方法的优势。

使用可调用类进行依赖注入

您可以使用可调用类来实现相同的功能,如下所示。

初始化可调用类的对象后,您可以像下面展示的基于函数的 DI 一样使用它:

使用可调用类的优势在于可以动态地将值传递给初始化程序,然后在依赖注入过程中利用这些值。在这种情况下,在创建 Pagination 类的实例时,将一个名为minimum_limit的值传递给初始化程序,并将其存储为对象变量,然后在依赖注入过程中使用。这种灵活性是使用可调用类的优势之一。

经进一步调查,发现可以在不需要可调用类的情况下使用该类进行依赖注入,实现类似的功能。如下所示,可以完全相同的方式实现常规函数(在本例中为get_offset_and_limit),以创建 Pagination 类的实例。

在配置路径操作函数时,不需要将pagination对象传递给Depends函数,只需按照下面所示传递pagination对象的get_offset_and_limit函数。

在实现到这一点之后,我觉得Python中的函数有着强大的“函数指针”定位。在C语言的基础上,这让我再次欣赏Python。

通过路由进行依赖注入

我发现的另一个功能是能够为每个路由配置依赖注入(DI),当您想要执行类似于常见中间件的过程时,这似乎特别有用,比如在多个API中跨请求头验证信息(类似于Node.js中的中间件)。

首先,您需要设置一个可调用类来接收 DI,如下所示。这个 ApiTokenHeader 类允许通过其初始化程序设置 API 令牌。

在 DI 过程中,将请求标头中名为 'api-token' 的值注入到名为 api_token 的变量中。请注意,标头名称不能包含下划线(_),但 FastAPI 会自动将连字符转换为下划线,因此您可以使用标头名称 'api-token' 发送请求。如果此 api_token 与类初始化程序中设置的值(self.api_token)匹配,则过程正常完成,API 的操作(路径操作函数的过程)将继续。如果不匹配,则返回 403 Forbidden 错误。

要应用此 DI 可调用类,请在创建 APIRouter 对象时将其传递给 Depends() 函数,然后将其设置为 dependencies 列表中的一部分,如下所示。

在完成这个设置后,DI 可调用类将应用于此后添加到路由器的所有 API。这意味着现在每个 API 都将对标头中的 'api-token' 进行检查。这非常方便,因为它消除了需要为每个 API 单独编写标头的令牌验证。如下所示,当 'api-token' 设置正确时,响应将成功返回。

(为了清晰起见,这次我实现了一个简单的标题令牌检查。请注意,这并不能保证足够的安全性来保护实际的API。)

通过将 DI 机制附加到每个路由器,可以像这样将不需要标头检查的开放 API 添加到单独的 APIRouter 对象中,从而区分安全级别。

在这种情况下,可以轻松地创建一个区分,其中"/public" 用于开放的API,而"/user" 需要标题验证。

如下所示,您可以在不包含标头的情况下访问位于"/public"下的API,并仍然收到响应。

另一方面,如果您在没有头信息的情况下访问位于/user下的API,或者发送错误的令牌,您将收到HTTP状态码= 403 Forbidden。

通过这个过程,我已经相当扎实地掌握了如何使用FastAPI和依赖注入(DI)构建产品所需的API的方法。

事实上,我并不总是对“依赖注入”这个术语有一个好印象。我觉得引入依赖注入框架或类似 Dagger 的标记到 Android 应用中可能会使代码变得更难阅读,降低编程的自由。此外,除非由一定质量水平的团队开发,否则它可能成为 bug 的温床,使调试变成一场噩梦。曾经有一次,我被委以任务挽救一个严格使用 Dagger 编写的应用,我不得不费力地解开每个依赖关系并纠正混乱的内存分配。

这次在 FastAPI 中实现了 DI 后,我意识到依赖注入对于 REST API 来说非常方便,因为会话的开始和结束都有明确定义。我也明白了为什么依赖注入首先在 Java 后端框架社区中变得流行起来。

相反,考虑是否真的是开发安装的移动应用程序的最佳解决方案 — 在那里有大量的用户交互,应用程序或屏幕的生命周期非常动态和不可预测 — 使用依赖注入,这是一个发人深省的经历。我想,通过 DI 进行集中管理的动机是因为众多的交互和生命周期模式,但这让我重新考虑,是否许多人可以使用 DI,并对集中管理的组件将如何使用有一个完美的想象。

敬请关注!

如果您对本文或我们的博客有任何疑问,请随时通过电子邮件与我联系:[email protected]

顺便说一下,我在 Goldrush Computing 工作,这是一家位于东京和越南胡志明市的移动应用和网站开发公司。我们目前专注于创建专门利用 OpenAI API 处理数据和文档的网络服务和后端系统。如果您正在寻找外部团队来实现您的产品,请随时与我们联系!

总结
本文介绍了在FastAPI中使用依赖注入(DI)的方法。通过函数和可调用类实现依赖注入,以及通过路由器配置依赖注入。依赖注入可用于验证请求头信息等中间件操作,提高代码灵活性和可维护性。文章强调了使用依赖注入构建REST API的便利性,尤其适用于Java后端框架。作者对依赖注入在移动应用程序开发中的适用性进行了思考,并分享了Goldrush Computing公司的业务方向。