前言

依赖注入系统,可以说是fastapi框架中很重要也是一个核心的系统。因为fastapi主要用的都是函数式编程实现API的方式,不像Django里面,有FBV和CBV。为了实现和Django一样的视图编程方式,于是就引入了这个依赖注入的概念。

依赖注入的优势:

  • 提高代码的复用率,一些公共的资源,比如,函数,提取出来复用,一些pydantic验证也是如此。
  • 共享数据库连接,避免操作数据库CURD时创造多条连接。
  • 增强安全、认证和角色管理,就比如,超级管理员拥有什么权限。管理员拥有超级管理员之中的某些权限,而用户又只拥有管理员之下的某些权限。

从这个fastapi框架的角度来看,他的兼容性也是非常的强大的。通过依赖注入系统,可以支持SQL数据库,和NoSQL数据库。

声明依赖项

我们不以复杂的理论和代码来论证这个依赖注入,我们声明一个函数,用来当作依赖项来进行讲解。

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

这里,我们声明了一个函数,有三个参数,qskiplimit,我没有对他进行任何处理,只是原封不动的返回了,如果在真实的开发场景中,我们如果把所有的业务处理全部放在一个函数中,那就成了祖传代码,除了你自己,几乎别人没人看得懂。(PS:你自己也不一定看得懂了~~(●’◡’●))

好了,回到我们这个场景,假如,我们要多次传入这三个参数,我们按照正常来说,也可以这么写:

async def test1(q: Optional[str] = None, skip: int = 0, limit: int = 100)
                
async def test2(q: Optional[str] = None, skip: int = 0, limit: int = 100)

然后,你会发现你的代码重复了,如果要进行相同的校验或者业务逻辑处理,需要更加繁琐的代码,也是需要一一重复。

这时候,我们把这个当作依赖,直接注入到函数中,就会减少重复。

@app.get('/')
async def test(common_parameters:dict = Depends(common_parameters)):
    return common_parameters

这样,我们同样传入这三个参数,但是,在这个函数中,相应的减少了一些业务代码的处理。

类作为依赖项

fastapi不只是可以将函数作为依赖项,一切可以被称为资源的,都可以作为依赖项。比如,类。用法,几乎是一致的。

class common_parameters:
    def __init__(self,q:Optional[str] = None,skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit

@app.get('/')
async def test(common_parameters:dict = Depends(common_parameters)):
    return common_parameters

子依赖

可以理解为类的继承, 声明了一个依赖项,经过一些处理后,再被另一个依赖给处理,有点像二次加工

# 声明一个子依赖项
def query_extractor(q: Optional[str] = None):
    return q

# 声明第二个依赖项,将子依赖作为依赖注入
def query_or_cookie_extractor(
    q: str = Depends(query_extractor), last_query: Optional[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项目的实例还是APIrouter的实例,所导的路径装饰器中,都可以进行依赖的注入。在路由中,使用dependencies来进行依赖的注入。他可以是一个Depends,也可以是一个列表,来进行多个依赖项的注入

# 声明两个依赖
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"}]

全局依赖

在全局中,无论是APIRouter还是FastAPI,他们所实例的变量都可以通过dependencies来进行依赖的注入,这样,所有使用了他们的路由装饰器,都会进行其依赖的操作。

# 在FastAPI中使用依赖注入
app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])

# 在APIRouter中使用
apirouter = APIRouter(dependencies=[Depends(verify_token), Depends(verify_key)])

关于fastapi

对于fastapi,我个人把他分成了几个部分,如果有感觉模糊不清的道友,可以参照这个思路,fastapi就非常的好理解了。

  1. 验证部分
    这里分为了几个验证部分,分别为:Body、Header、Path、Query、Cookie,其中,Body包括了pydantic,这里需要注意的一点就是,pydantic和fastapi不是一体的。不要搞混了。
  2. 请求部分
    关于对请求的处理,这里也包括了对Path的验证
  3. 依赖注入系统
    这个就不用多说了,😄
  4. 响应处理
    包括json、表单、文件等请求或者响应,这个不应该单独分出来,应该是和请求部分一致学习的
  5. 认证授权数据库等

只要是好好的给他分割开来,逐一击破,对于新的框架学习来说,学习成本实际上并没有这么的高。