fastapi(十六)-依赖关系

本文深入探讨了FastAPI框架中的依赖注入系统,解释了依赖注入的概念,如何在代码中声明和使用依赖,以及依赖注入的强大功能,包括嵌套依赖、多次使用相同依赖和在路径操作装饰器中使用依赖。

fastapi是一个非常强大而又直观的依赖注入系统
它被设计得使用起来非常简单,同时让开发人员可以轻松得将其他组件与fastapi集成在一起。
什么时依赖注入
依赖注入意味着在编码得时候,在你得代码中会留一条通道给你去声明需要用到依赖,(即:要想你得代码成功运行必须得先成功运行依赖函数。有点类似于装饰器)
在你做以下操作得时候将会非常有用:

  • 使用共享逻辑(一遍又一遍得使用相同得代码)
  • 共享数据连接
  • 强制安全,授权,角色需要等

第一步:
让我们先看一个例子,这个例子在实际应用中根本没什么用,但是可以关注一下依赖注入是如何运行得。
首先建立被依赖得函数,它仅仅是一个函数它可以拥有所有得路径函数得参数

from fastapi import Depends, FastAPI

app = FastAPI()


async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


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


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

它和路径操作函数拥有相同得结构,你甚至可以理解为,它就是没有装饰器得路径操作函数。
并且它可以返回你想要得任何东西,
在这个例子中被依赖函数将希望接收:
1、一个可选择得查询参数q,str类型
2、一个带有默认值为0得skip查询参数,int类型
3、一个带有默认值为100得limit查询参数,int类型
然后将他们转换为字典再返回

声明依赖,
依赖得声明就和Body,Query等一样,需要使用Depends。
但是他们也有一些不同得地方:
depends中只有一个被依赖得参数,
无论什么时候请求到达后,fastapi将做一下操作:
1、调用依赖函数,并使用正确得参数。
2、从函数中得到返回。
3、分配返回结果给你得路径操作函数得参数。

简单得使用
和所有得web框架一样,路径操作函数被声明来匹配路径,然后fastapi将调用函数并传入正确得参数,并处理响应。
你并不会直接得调用这些函数,他们被框架调用。
在依赖注入系统中,你可以告诉fastapi你得路径操作函数依赖于其他得东西。并且fastapi将自动处理并注入结果。
其它使用依赖注入思想的条目:

  • resource
  • providers
  • services
  • injectables
  • components

简单而强大
尽管依赖注入系统定义和使用上都非常简单,但是它却非常的强大。
你可以嵌套依赖,
最后,构建了层次结构的依赖关系树,并且依赖关系注入系统将为您解决所有这些依赖关系(及其子依赖关系)并在每个步骤中提供(注入)结果。

例如,假设您有4个API端点(path操作):

/items/public/
/items/private/
/users/{user_id}/activate
/items/pro/
那么您可以为每个依赖项和子依赖项添加不同的权限要求:

在这里插入图片描述
类作为依赖项
直到现在,所使用的都是依赖被声明为函数。
但是那并不是唯一的依赖声明。
实际上依赖只要能被“callable”就行。
在python中这叫鸭子类型,只要实现了__call__()函数的所有类型都可以是callable。

from fastapi import Depends, FastAPI

app = FastAPI()


fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


class CommonQueryParams:
    def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
    response = {}
    if commons.q:
        response.update({"q": commons.q})
    items = fake_items_db[commons.skip : commons.skip + commons.limit]
    response.update({"items": items})
    return response

这些类中的初始化参数都是从requests中获得。

type annotation vs depends

以上的例子中commons定义:
commons: CommonQueryParams=Depends(CommonQueryParams)

实际可以使用:
commons=Depends(CommonQueryParams)
以及:
commons: CommonQueryParams = Depends()
都是一样的效果。

子依赖
你可以创建依赖嵌套,而且想嵌套多少层都可以。
eg:

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()


def query_extractor(q: str = None):
    return q


def query_or_cookie_extractor(
    q: str = Depends(query_extractor), last_query: str = Cookie(None)
):
    if not q:
        return last_query
    return q


@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
    return {"q_or_cookie": query_or_default}

多次使用相同依赖
例如,如果为同一个路径操作多次声明一个依赖项,例如,多个依赖项具有一个公共的子依赖项,则FastAPI将知道每个请求仅调用一次该子依赖项。

并且它将返回的值保存在“缓存”中,并将其传递给该特定请求中需要它的所有“依赖项”,而不是针对同一请求多次调用依赖项。

在高级场景中,您需要在同一请求中的每个步骤(可能多次)上调用依赖项,而不是使用“ cached”值,可以use_cache=False在使用时设置参数Depends:

async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {“fresh_value”: fresh_value}

依赖在路径操作装饰器中

有时候你的依赖中其实并不需要返回值。在这种情况下你可以将依赖添加到路径操作装饰器中的list。

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()


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


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

这些依赖将会被执行,但是他们的返回值将不会传入路径操作函数中。
Dependencies with yield
fastapi支持依赖在结束的时候执行一些额外的步骤。
为了实现此功能,需要将return换成yield,然后写入额外的步骤。
例如:
你可以创建数据库连接然后在完成的时候关闭它。

async def get_db():
    db = DBSession()
    try:
        yield db
    finally:
        db.close()

Dependencies with yield and HTTPException

上一个例子中你可以看到,可以使用yield语句并且使用try去捕获异常。
你也许想要去抛出HTTPException或退出码,在yeild中,但是这将不会起作用。
在这里插入图片描述
只有一种response将会被发送到客户端,那就是error response 或者路径操作的response。一旦这些response被发送后,其他的response将不会被发送到客户端。

上下文管理器

什么是上下文管理器,其实就是多有的可以使用with语句的python对象:

with open("./somefile.txt") as f:
    contents = f.read()
    print(contents)

当with 块结束后,可以保证关闭文件,即使有异常。

当你使用yield语句创建依赖,fastapi将会在内部将它转换为上下文管理器,并将它与其他一些相关联的工具相结合。
Using context managers in dependencies with yield
在python中你可以创建上下文管理器,通过在对象内部定义__enter__()和__exit__()方法。

class MySuperContextManager:
    def __init__(self):
        self.db = DBSession()

    def __enter__(self):
        return self.db

    def __exit__(self, exc_type, exc_value, traceback):
        self.db.close()


async def get_db():
    with MySuperContextManager() as db:
        yield db

你也可以在fastapi中的with或者async with语句中使用yield来定义依赖函数。
其他的定义上下文管理器的方式:
@contextlib.contextmanager
@contextlib.asynccontextmanager
使用这些装饰器并使用yield做返回。但是在fastapi中你不需要使用装饰器,直接使用yield语句。

高级依赖
参数依赖
我们看到的所有依赖项都是固定的函数或类。
但是在某些情况下,您可能希望能够在依赖项上设置参数,而不必声明许多不同的函数或类。
假设我们想要一个依赖项来检查查询参数是否q包含一些固定内容。
但是我们希望能够参数化该固定内容。

from fastapi import Depends, FastAPI

app = FastAPI()


class FixedContentQueryChecker:
    def __init__(self, fixed_content: str):
        self.fixed_content = fixed_content

    def __call__(self, q: str = ""):
        if q:
            return self.fixed_content in q
        return False


checker = FixedContentQueryChecker("bar")


@app.get("/query-checker/")
async def read_query_check(fixed_content_included: bool = Depends(checker)):
    return {"fixed_content_in_query": fixed_content_included}

__call__就是FastAPI将用于检查其他参数和子依赖项的方法,并且稍后将调用该方法将值传递给路径操作函数中的参数。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值