依赖的介绍
依赖项可以是一个函数,也可以是一个类。通过在路径操作函数中引入依赖项,可以达到先调用依赖项,并将依赖项调用的结果作为路径操作函数的输入。 下面通过一个示例来引出依赖项。 当多个路径操作函数中都具有相同的处理逻辑时,可以将共同的处理逻辑提取出来,作为依赖项。然后将这个依赖项引入各个路径操作函数中。这样就可以复用代码,将代码重复最小化。 依赖项通常使用在如下场景:
- 共享业务逻辑(复用相同的代码逻辑)
- 共享数据库连接
- 实现安全、验证、角色权限等
函数依赖
当依赖项是一个函数的时候,此时依赖项就是一个函数依赖。 常见和使用函数依赖主要有以下步骤: 步骤1:创建依赖函数 示例中创建一个返回字典的函数。
async def common_parameters(
q:Union[str,None] = None,
skip:int = 0,
limit:int = 100
):
return {
"q":q,
"skip":skip,
"limit":limit
}
步骤2:在路径操作函数中使用依赖项
@app.get("/items")
async def read_items(commons:dict = Depends(common_parameters)):
return commons
完整代码:
from typing import Union
from fastapi import Depends,FastAPI
app = FastAPI()
async def common_parameters(
q:Union[str,None] = 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
if __name__ == "__main__":
import uvicorn
uvicorn.run(app="main:app",host="127.0.0.1",port=8080,reload=True)
这个示例中路径操作函数执行过程:
- 执行common_parameters函数,并得到返回值commons
- 路径操作函数将commons作为输入,执行其函数体中的逻辑
通过这个执行过程可以看出依赖项,在路径操作函数执行前,进行了一些逻辑处理。 或者可以说路径操作函数的结果依赖于依赖项的执行结果。
有一点需要新手注意:依赖项的输入参数是路径操作函数的一部分! 本示例验证结果:
类依赖
在fastapi中不仅仅函数可以作为依赖项,类也可以作为依赖项。这里仅修改函数依赖中的代码进行说明。 依赖也分两步使用: 步骤1:创建公共类
class CommonQueryParams:
def __init__(self,q:Union[str,None]=None,skip:int = 0,limit:int = 100):
self.q = q
self.skip = skip
self.limit = limit
步骤2:路径操作函数中引用公共类
@app.get("/item")
async def read_item(common:CommonQueryParams=Depends(CommonQueryParams)):
return jsonable_encoder(common)
完整代码如下:
from typing import Union
from fastapi import Depends,FastAPI
from fastapi.encoders import jsonable_encoder
app = FastAPI()
class CommonQueryParams:
def __init__(self,q:Union[str,None]=None,skip:int = 0,limit:int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/item")
async def read_item(common:CommonQueryParams=Depends(CommonQueryParams)):
return jsonable_encoder(common)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app="main:app",host="127.0.0.1",port=8080,reload=True)
测试结果如下:
对比函数依赖和类依赖的路径操作函数,需要注意: 在函数依赖中路径操作函数参数的类型声明为依赖函数的返回结果类型,Depends函数的参数是函数名 在类依赖中路径操作函数参数的类型声明为依赖类的类名,Depends函数的参数是依赖类名
嵌套依赖
fastapi中支持创建含依赖项的依赖项,并可以按需声明任意深度的子依赖项嵌套层级。
def query_extractor(q:Union[str,None] = None):
print("q")
return q
def query_or_cookie_extractor(
q:str=Depends(query_extractor),
last_query:Union[str,None]=Cookie(default=None)
):
print("cookie")
if not q:
return last_query
return q
@app.post("/items")
def create_items(query_or_default:str = Depends(query_or_cookie_extractor)):
return {"q_or_cookie":query_or_default}
从下图演示结果可以看出,fastapi运行依赖项时,最先运行最里层的依赖,然后依次运行外层的依赖项。
当路径操作函数中存在依赖项时,该路径操作函数的输入参数就是所有依赖项参数的和路径操作函数本身参数的集合。 上例的路径参数的输入参数是q查询参数和last_query cookie参数。
多次使用同一个依赖项
如果在同一个路径操作 多次声明了同一个依赖项,例如,多个依赖项共用一个子依赖项,FastAPI 在处理同一请求时,只调用一次该子依赖项。
FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行「缓存」,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。
在高级使用场景中,如果不想使用「缓存」值,而是为需要在同一请求的每一步操作(多次)中都实际调用依赖项,可以把 Depends 的参数 use_cache 的值设置为 False
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {"fresh_value": fresh_value}
路径操作装饰器依赖项
当依赖项没有返回值,但是仍要执行或解析该依赖项时,就不必在声明路径操作函数的参数时使用Depends,而是可以在路径操作装饰器中添加一个由dependencies组成的列表。
async def verify_token(x_token:str=Header()):
if not x_token:
raise HTTPException(status_code=400,detail="not X-Token header invalid")
async def verify_key(x_key:str=Header()):
if not x_key:
raise HTTPException(status_code=400,detail="not X-Key header invalid")
@app.get("/student",dependencies=[Depends(verify_key),Depends(verify_token)])
async def read_student():
return {"msg":"hello"}
从测试结果可以看出,这里的依赖都会被执行一次。
全局依赖
当依赖项被每个路径操作函数依赖时,我们可以通过添加全局依赖的方式为每个路径操作函数添加依赖。而避免了在每个路径操作函数或路径装饰器中添加依赖。 这主要是通过在FastAPI创建实例对象时使用dependencies参数。
app1 = FastAPI(dependencies=[Depends(verify_key),Depends(verify_token)])
@app1.get("/student")
async def read_student():
return {"msg":"hello"}
|