FastAPI dependency injection system

本文最后更新于大约 1 年前,文中所描述的信息可能已发生改变。

什么是「依赖注入」

What is "Dependency Injection"

编程中的「依赖注入」是声明代码(本文中为路径操作函数 )运行所需的,或要使用的「依赖」的一种方式。

然后,由系统(本文中为 FastAPI)负责执行任意需要的逻辑,为代码提供这些依赖(「注入」依赖项)。

依赖注入常用于以下场景:

  • 共享业务逻辑(复用相同的代码逻辑)
  • 共享数据库连接
  • 实现安全、验证、角色权限等……

上述场景均可以使用依赖注入,将代码重复最小化。

"Dependency Injection" means, in programming, that there is a way for your code (in this case, your path operation functions) to declare things that it requires to work and use: "dependencies".

And then, that system (in this case FastAPI) will take care of doing whatever is needed to provide your code with those needed dependencies ("inject" the dependencies).

This is very useful when you need to:

  • Have shared logic (the same code logic again and again).
  • Share database connections.
  • Enforce security, authentication, role requirements, etc.
  • And many other things…

All these, while minimizing code repetition.

示例

Examples

函数作为依赖

Function as dependence

python
from fastapi import FastAPI, Depends

app = FastAPI()

async def common_parameters(q: str | None = None, page: int = 1, limit: int = 10):
    return {"q": q, "page": page, "limit": limit}

@app.get("/dependency")
async def dependency(commons: dict = Depends(common_parameters)):
    return commons

common_parameters 函数定义了一组通用的查询参数(q,page,limit),该函数接受这些参数并返回一个字典,该字典包含这些参数及其值。这些参数之间可以通过逗号进行分隔,表示它们是可选参数,并且有默认值。

@app.get("/dependency") 的路由装饰器绑定了一个API路由,这个路由接受一个名为 commons 的参数,这个参数需要调用 common_parameters 函数的返回值。Depends可以让我们把一个函数当做参数来传递到另一个函数,这里的 Depends(common_parameters) 表示该API路由需要调用 common_parameters 来获取查询参数。

当请求到达API路由时,FastAPI将调用 common_parameters 函数,将返回的字典作为 commons 参数的值注入到 dependency 函数中,然后执行处理请求的逻辑并返回响应。因此,dependencycommons 参数包含所有通用查询参数的值,实现了依赖注入的功能。

也就是说,通过依赖注入,我们使得一个代码块(函数)中可以调用另一个代码块,并访问其中的变量或属性

TIP提示

实际上,这里是实现了类似类的继承的功能,由于 FastAPI 是以函数式编程为主,所以才有了依赖注入系统,来实现类的一些功能.

在上面的例子中,common_parameters 可以看作是一个公共的基类(类似于父类),它定义了一组通用的查询参数。同时,@app.get("/dependency") 中的函数可以看作是派生类(类似于子类),它需要使用通用查询参数来处理请求。

当调用dependency函数时,Depends 会自动调用 common_parameters 函数,获取查询参数,然后将其注入到 dependency 函数中,类似于子类调用父类的构造函数获取公共配置,并继承其行为的方式。

虽然Depends和依赖注入并不是类的继承,但它们可以实现类似的效果,使代码更加清晰和易于维护。

The function common_parameters defines a set of common query parameters (q, page, limit) and accepts these parameters as input, then returns a dictionary containing these parameters and their values. These parameters can be separated by commas to indicate they are optional and have default values.

The API route decorator @app.get("/dependency") binds an API route that accepts a parameter named commons, which needs to call the common_parameters function to get the query parameters. Depends allows us to pass a function as a parameter to another function. Here, the Depends(common_parameters) indicates that this API route needs to call common_parameters to get the query parameters.

When a request arrives at the API route, FastAPI will call the common_parameters function and inject the returned dictionary as the value of the commons parameter into the dependency function. Then, it executes the logic to handle the request and returns the response. Therefore, the commons parameter of the dependency function contains the values of all the common query parameters, achieving the functionality of dependency injection.

TIP提示

In fact, here we are implementing a function similar to class inheritance. Since FastAPI is mainly based on functional programming, it has the dependency injection system to implement some functionality similar to class inheritance.

In the example above, common_parameters can be seen as a common base class (similar to a parent class) that defines a set of common query parameters. At the same time, the function in @app.get("/dependency") can be seen as a derived class (similar to a child class) which needs to use the common query parameters to handle requests.

When calling the dependency function, Depends automatically calls common_parameters to get the query parameters and injects them into the dependency function, similar to calling the parent class constructor to get common configurations and inherit its behavior.

Although Depends and dependency injection are not inheritance of classes, they can achieve similar effects, making the code more clear and easy to maintain.

类作为依赖

Class as dependence

python
fake_item_db = [{"item": "foo"}, {"item": "qwq"}, {"item": "awa"}]

class CommonQueryParams:
    def __init__(self, q: str | None = None, page: int = 1, limit: int = 100) -> None:
        self.q = q
        self.page = page
        self.limit = limit


@app.get("/classes_as_dependencies")
async def class_as_dependencies(commons=Depends(CommonQueryParams)):
    resp = {}
    if commons.q:
        resp.update({"q": commons.q})
    items = fake_item_db[commons.page:commons.limit]
    resp.update({"items": items})
    return resp
# 下面这两种写法也可以将类作为依赖
# async def class_as_dependencies(commons:CommonQueryParams=Depends(CommonQueryParams))
# async def class_as_dependencies(commons:CommonQueryParams=Depends())

当用户访问 /classes_as_dependencies 时,CommonQueryParams 依赖项会注入为 commons 参数。该端点然后检查请求中是否有指定搜索查询,并在有指定查询时使用它来更新 resp 字典。然后它根据 page 和 limit 参数从 fake_item_db 数据库检索项目,并返回一份带有搜索查询内容(如果有的话)和检索到的项目的响应。

python
fake_item_db = [{"item": "foo"}, {"item": "qwq"}, {"item": "awa"}]

class CommonQueryParams:
    def __init__(self, q: str | None = None, page: int = 1, limit: int = 100) -> None:
        self.q = q
        self.page = page
        self.limit = limit


@app.get("/classes_as_dependencies")
async def class_as_dependencies(commons=Depends(CommonQueryParams)):
    resp = {}
    if commons.q:
        resp.update({"q": commons.q})
    items = fake_item_db[commons.page:commons.limit]
    resp.update({"items": items})
    return resp

When a user accesses the /classes_as_dependencies endpoint, the CommonQueryParams dependency is injected as the commons parameter. The endpoint then checks if there is a search query specified in the request and updates the resp dictionary with the query if there is one. It then retrieves items from the fake_item_db database based on the page and limit parameters, and returns a response with the search query (if any) and the retrieved items.

嵌套依赖

nested dependencies

依赖之间可以嵌套,一个依赖可以依赖于另一个依赖,即称为子依赖

路径装饰器依赖与全局依赖

Path decorator dependencies and global dependencies

可以为 FastAPI 的路径装饰器传入 dependencies 参数,该参数的值是 Depend() 类列表 ,传入后可以在装饰器下的函数中调用依赖项

也可以在创建 FastAPI 或 APIRouter 实例时导入依赖,同样是传入 dependencies 参数,这样的依赖在整个应用下都可以调用

You can pass the "dependencies" parameter to the path decorator of FastAPI, and the value of this parameter is a "Depend()" class list. After passing it, the dependencies can be called in the function under the decorator.

You can also import dependencies when creating FastAPI or APIRouter instances by passing the "dependencies" parameter. Such dependencies can be called throughout the entire application.

JWT 认证及其在 FastAPI 中的使用
ArchLinux on Y9000P2022十分好用,就是有点难用
Valaxy v0.18.7 驱动 | 主题 - Yun v0.18.7
本站已勉强运行0 天0 小时0 分0 秒(ノ`Д)ノ