依赖注入是耳熟能详的一个词了,听起来很复杂,实际上并没那么复杂,正常的访问需要接受各种参数来构造一个对象,依赖注入就变成了只接收一个实例化对象,主要用于共享业务逻辑、共享数据库连接、实现安全、验证、权限等相关的业务功能,本文主要记录一下fastapi的依赖注入。

函数依赖项

# 创建、导入、声明依赖
async def user_verification(
        user: str,
        birthday: date,
        age: int
):
    return {
        "user": user,
        "birthday": birthday,
        "age": age
    }


# 同时依赖于user_verification
@app10.get("/user_verification/test1")
async def user_verification_test1(
        test1: dict = Depends(user_verification)
):
    return test1


@app10.get("/user_verification/test2")
async def user_verification_test2(
        test2: dict = Depends(user_verification),
        test2_param:Optional[str] = None
):
    return {
        "test2":test2,
        "test2_param":test2_param
    }

上面代码就完成了一个依赖项函数的创建和调用,首先是创建了一个依赖项函数user_verification,函数接受三个参数,并将三个参数以字典的形式返回;

后面的两个路径操作函数在本质上是一样的,对于test1,定义一个dict类型的参数test1用于接收依赖项函数的返回值,然后将test1进行返回,test2除了要接受依赖项user_verification需要的参数以外,还接受路径修饰函数的test2_param参数,并将其返回。

这个示例就很好的示例了依赖项函数的使用,fastapi的依赖注入系统会自动处理所有的依赖项及其子依赖项,并为每一步操作都注入结果。

  • http://127.0.0.1:8000/stu/user_verification/test1?user=MinChess&birthday=2021-12-23&age=11

image-20221222180127785

  • http://127.0.0.1:8000/stu/user_verification/test2?test2_param=%E6%9C%AC%E8%BA%AB%E7%9A%84%E4%B8%80%E4%B8%AA%E5%8F%82%E6%95%B0&user=%E4%B9%9D%E9%99%8C%E6%96%8B&birthday=2012-12-03&age=23

image-20221222181048641

类依赖项

# 类作为依赖项
class User:
    def __init__(self, name: str, age: Optional[str], birthday: date):
        self.name = name
        self.age = age
        self.birthday = birthday


@app10.get("/stu10_UserClass")
async def stu10_UserClass(
        user: User = Depends(User)
):
    return user

上面定义了一个User类,在默认的构造函数中,除了默认的self参数,另外指定了三个参数,和上面的例子一样;进一步在路径修饰函数中指定user参数依赖于User类。

FastAPI调用User类,以此会创建该类的一个"实例",该实例作为参数user传递给路径修饰函数。

image-20221222181813704

多层依赖

# 多层依赖

def first_verification(
        param1: Optional[str] = None
):
    print(param1)
    return param1


def second_verification(
        param_1: str = Depends(first_verification),
        param_2: Optional[str] = None
):
    if not param_1:
        return param_2
    return param_1


@app10.get("/stu10_more_verification")
async def stu10_more_vberification(
        param: str = Depends(second_verification)
):
    return {"param": param}

这段代码包含三个函数,第一个函数是第一层依赖,声明了一个可选的参数param1,并将这个参数进行返回;

第二个参数是另一个依赖项函数,它自身还依赖于第一个依赖项函数,判断传入的值是哪一个,有值就返回;

最后一个路径操作函数,声明一个param参数,依赖于第二个依赖项函数,最后将param参数返回。

fastapi对于多层依赖,一层一层的处理,先处理第一个再处理第二个。

如果在同一个路径操作 多次声明了同一个依赖项,例如,多个依赖项共用一个子依赖项,FastAPI 在处理同一请求时,只调用一次该子依赖项。

FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行「缓存」,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。

image-20221223162757034

image-20221223162816998

路径操作装饰器依赖项

# 路径操作装饰器依赖项
async def verify_token(x_token: str = Header()):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")


async def verify_key(x_key: str = Header()):
    if x_key != "fake-super-secret-key":
        raise HTTPException(status_code=400, detail="X-Key header invalid")
    return x_key


@app10.get("/stu10/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
    return [{"item": "Foo"}, {"item": "Bar"}]

官方文档的解释是:有时,我们并不需要在路径操作函数中使用依赖项的返回值。

或者说,有些依赖项不返回值。

但仍要执行或解析该依赖项。

对于这种情况,不必在声明路径操作函数的参数时使用 Depends,而是可以在路径操作装饰器中添加一个由 dependencies 组成的 list

如上代码,在路径操作装饰器中添加 dependencies 参数,这个参数是由Depends()组成的list

它的解析和执行方法和普通的依赖项是一样的,但是它们的值不会传递给路径操作函数,不管有没有返回值,路径操作都不会使用这些值。

image-20221226162153311

全局依赖项

async def main_depends():
    print("main depends")


app = FastAPI(
    title='FastAPI学习教程文档——title',
    description='这是FastAPI教程的文档——description',
    version='1.0.0',
    docs_url='/docs',
    redoc_url='/redoc',
    dependencies=[Depends(main_depends)]
)

全局依赖项就是为整个应用添加依赖项,添加方式和定义路径装饰器依赖项类似,可以把依赖项添加到整个FastAPI主应用中。

如上就是在FastAPI应用中添加dependencies参数。

以此,所有的路径操作都会默认依赖上面的依赖项函数。

如下,随便访问一个路径,控制台都会打印main depends

image-20221226163722379

依赖项中使用 yield

# 依赖项中使用yield
async def get_yield():
    try:
        yield "yield param"
    finally:
        print("yield end!")


@app10.get("/stu10/yield")
async def stu10_yield(
        yieldparam: str = Depends(get_yield)
):
    return {
        "yield_param": yieldparam
    }

FastAPI 支持在依赖项返回后执行一些额外的步骤,但需要用 yield 代替 return 来达到这一目的。

也就是该依赖项函数返回值后还需要进行一些操作,这个时候就需要利用yield关键字。

如上代码,返回yield param后继续操作打印yield end!这里主要记录一下yield的简单使用,更多的可以参看下面的官网简介。

同样也可以多层依赖,还有很多底层的内容,可以参看官网:https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/

image-20221226182224122


感谢阅读!

九陌斋地址:https://blog.jiumoz.com/archives/fastapi-cong-ru-men-dao-shi-zhan-16-yi-lai-xiang

欢 迎 关 注 博 主 个 人 小 程 序!