Posted on:
Last modified:
依赖注入用于把一些可复用的逻辑抽离出来,减少代码重复。例如,返回列表的 API 中都会用到 page 和 page_size 这几个参数,那么可以创建一个依赖来包含这两个参数。
还可以使用依赖来做插件,如在依赖中做一些 Token 的验证等等。所以,在 FastAPI 中,不需要 插件系统,只要把要复用的逻辑实现为一个依赖就好了。
依赖的定义是一个 callable, 也就是说函数或者类都可以。依赖的参数和每一个 handler 的参数都 一样,所以 GET/POST/Cookie/Header 等参数都可以以相同的方式使用。
# 使用函数作为依赖
from fastapi import Depends
async def pagination(page: int, size: int):
return {"page": page, "size": size}
@app.get("/users")
def get_users(pagination: dict=Depends(pagination)):
users = user_model.get(**pagination)
return users
@app.get("/items")
def get_items(pagination: dict=Depends(pagination)):
items = items_model.get(**pagination)
return items
使用类有一个好处,你可以把这个类作为类型注释,这样和其他的参数使用方式更加一致。但是也有
一个缺点,那就是函数的 __init__
必须是 sync 的,可能会在线程池中执行。
class Pagination:
def __init__(self, page: int, size: int):
self.page = page
self.size = size
@app.get("/users")
def get_users(pagination: Pagination=Depends(Pagination)):
users = user_model.get(**pagination)
return users
# 比较巧妙的一点,我们可以省掉 Depends 的参数
def get_users(pagination: Pagination=Depends()):
...
Pydantic 的 model 也可以作为依赖直接使用,这样就相当于把 GET 参数作为一个 model 了。
class Pagination(BaseModel):
page: int
page_size: int
@app.get("/items")
def list_items(pagination: Pagination = Depends()):
...
依赖可以在三个地方添加:handler 函数参数,路径装饰器,全局 app 实例。如果在 handler 函数的 参数中添加,那么依赖的返回值会作为参数传递进去,就像其他参数一样。其他两种方式返回值都会被丢弃。
from fastapi import FastAPI, APIRouter, Depends
class Dependable:
def __init__(self, ...):
...
# 全局依赖
app = FastAPI(dependencies=[Depends(Dependable), ...])
# 在路径装饰器中添加一个依赖数组
@app.get("/", dependencies=[Depends(Dependable), ...])
def home(param: Dependable = Depends()): # 在 handler 参数中使用
...
# 在 Router 中使用
router = APIRouter(dependencies=[Depends(Dependable)])
依赖还可以有依赖,也就是依赖的参数也可以是 Depends
def get_user(username: int, token: str = Depends(verify_token)):
...
见前边 pagination 的例子
from fastapi import Depends, FastAPI, Header, HTTPException
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")
app = FastAPI(dependencies=[Depends(verify_token)])
这个例子可以进一步扩展成使用 session.
我们需要使用 yield 来返回创建的实例,当 handler 函数执行完毕之后,yield 后边的语句才会 继续执行(关闭数据库)。请特别注意,不要在这里执行 commit,应该由业务代码在 return 之前 执行,否则可能会造成给前端返回的数据不一致的问题。
async def get_db():
db = DBSession()
try:
yield db
except Exception:
db.rollback()
raise
finally:
db.close()
如果是 redis,可以这样:
REDIS_URL = "redis://localhost:6379/0"
redis_pool = ConnectionPool.from_url(REDIS_URL)
def get_redis():
return Redis(connection_pool=redis_pool)
当依赖之间有依赖关系的时候,可能会出现有两个依赖同时调用同一个上级依赖的问题,有点类似 继承中的多继承关系。
© 2016-2022 Yifei Kong. Powered by ynotes
All contents are under the CC-BY-NC-SA license, if not otherwise specified.
Opinions expressed here are solely my own and do not express the views or opinions of my employer.
友情链接: MySQL 教程站