五分钟掌握 FastAPI “依赖注入”
FastAPI 系列
- 五分钟掌握 FastAPI 文件上传
- FastAPI 子应用
- FastAPI + SQLAlchemy 异步编程
- 使用 FastAPI + React + ZhipuAI 实现一个的AI聊天应用
- 10分钟掌握 FastAPI 与 SQLAlchemy + alembic 一套数据库操作
开始之前
先看看本文简单小结:
一、什么 FastAPI 的依赖注入
依赖注入是一种用于解耦组件并实现代码复用的机制。
在 FastAPI 中依赖注入与 class 编程风格不一样,FastAPI 会自动将需要注入的函数,注入到依赖试图中,无需手动注入。
二、开始之前我们需要哪些基础知识?
- Python
Callable
type hits - 如果你熟悉其他的框架的依赖注入,下面的概念应该特别熟悉,同样它们也适用于 FastAPI 只是表现形式不一样而已:
- 资源(Resource)
- 提供方(Provider)
- 服务(Service)
- 可注入(Injectable)
- 组件(Component)
三、依赖注入使用场景
- 数据库注入 db
- 数据校验注入 token
- ...
四、什么可以作为依赖
写一个依赖可以是 class 也可以是 function。下面我们简单看看 class 和 function 依赖有什么不同。
类依赖
from fastapi import FastAPI, Depends
app = FastAPI()
class MyDepends:
def __init__(self, page: int = 1, page_size: int = 10):
self.page = page
self.page_size = page_size
@app.get("/")
async def root(item: MyDepends = Depends(MyDepends)):
return {"message": "Hello World", "page": item.page, "page_size": item.page_size}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, port=8000)
函数依赖
函数依赖只最为常见的依赖,当有需求的时候,依赖自动调用此函数:
from fastapi import FastAPI, Depends
app = FastAPI()
def get_page(page: int = 1, page_size: int = 10):
return {
"page": page,
"page_size": page_size
}
@app.get("/")
async def root(item: dict = Depends(get_page)):
return {"page": item["page"], "page_size": item["page_size"]}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, port=8000)
五、依赖形式
fastapi 中依赖有两种形式:
- Depends + 接口处理函数参数
- dependencies 配置项目
Depends 依赖
from fastapi import FastAPI, Depends
@app.get("/")
def read_query(query: str = Depends(get_query_param)):
return {"query": query}
作为一个路由的默认值。
dependencies 配置项目 + Depends
dependencies 可以在:
应用:FastAPI(dependencies=[])
路由:APIRouter(dependencies=[])
接口:async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
六、APP 依赖注入
一个简单的示例:
from fastapi import Depends, FastAPI
def get_db(a):
# do something
print("xx",a)
return True
app = FastAPI(dependencies=[Depends(get_db)])
@app.get("/")
async def read_root():
return {"Hello": "World"}
@app.post("/")
async def create_root():
return {"Hello": "World"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, port=8000)
两个接口 GET /
和 POST /
, 在 FastAPI 的 dependencies 注入了 get_db
, get 包含 a 参数此时,表示 url 的 params 参数。post 方法也是,我们看看 docs 的提示。
访问 http://127.0.0.1:8000/?a=1
不传递参数:
FastAPI 会报告一个错误,丢失一个 query 参数 a
。
七、router 和接口的依赖注入
Router 级有三种方式:router
的 dependencies
中 , 路由的 dependencies
和 路由处理函数 参数 中
:
from fastapi import Depends, FastAPI, APIRouter
def get_db(a):
# do something
print("xx",a)
return True
app = FastAPI()
router = APIRouter(dependencies=[Depends(get_db)])
@app.get("/", dependencies=[Depends(get_db)])
async def read_root():
return {"Hello": "World"}
@app.get("/app")
async def read_root_d(db: bool = Depends(get_db)):
return {"Hello": "World", db: db}
@router.post("/r")
async def create_root():
return {"Hello": "World"}
app.include_router(router, prefix="/router")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, port=8000)
八、多依赖 dependencies 配置项
通常使用 dependencies 来管理多依赖:
from fastapi import FastAPI, Depends
app = FastAPI()
def deps_one():
print("one")
pass
def deps_two():
print("two")
pass
@app.get("/items/", dependencies=[Depends(deps_one), Depends(deps_two)])
async def read_items():
return [{"item": "Foo"}, {"item": "Bar"}]
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, port=8000)
dependencies 传递数组即可, 一次传递依赖函数,那么实际情况中依赖的执行顺序是什么样的呢?
执行的顺序与列表的顺序一致。
九、依赖串
Depends 可以在函数参数处构成依赖串,这个实际项目中可能比较常用:
from fastapi import Depends, FastAPI
def get_next_line(a):
# do something
print("next", a)
return True
def get_db(a: str = Depends(get_next_line)):
# do something
print("xx", a)
return True
app = FastAPI()
@app.get("/")
async def read_root(a: str = Depends(get_db)):
return {"Hello": a}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, port=8000)
我们访问 http://127.0.0.1:8000/?a=gh
, 终端输出如下:
多依赖的输出顺序是:内部的依赖先输出,外部(靠近路由)后输出的的规则。
十、依赖与 yield
from fastapi import FastAPI, Depends, HTTPException
app = FastAPI()
def get_username():
try:
yield "Rick" # 定义一个简单的 yield 语句
except Exception as e:
raise HTTPException(status_code=400, detail=f"Owner error: {e}")
@app.get("/")
async def root(uname: str = Depends(get_username)):
return {"message": "Hello World"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, port=8000)
十一、源码
Depends 的来源:param_functions
中的 Depends:
Depends 调用了 params
中 Depends:
最终实现是: 需要一个 dependency 函数(当然是可选的)
十二、小结
本文较为详细讲解了 FastAPI 中的依赖注入,如果你使用过其他框架依赖注入,虽然表现形式上有所不同,但是所要表达的思想道理基本一致。FastAPI 的依赖项目通过 Depend 函数和 dependencies 配置项进行管理。支持函数配置和class配置。对于复杂的配置支持依赖串和多依赖支持。