FastAPI
传参
三种传参方式
@app.put("/items/{item_id}")
async def update_item(
item_id: int, # 1. 路径参数(URL路径中)
item: Item, # 2. 请求体参数(JSON body中)
q: Optional[str] = None # 3. 查询参数(URL查询字符串中)
):
return result
对应的请求格式:
PUT /items/123?q=urgent ← 路径参数 + 查询参数
Content-Type: application/json
{
"name": "apple", ← 请求体参数
"price": 5.5
}
依赖注入
- 普通方法/类作为依赖的,就是的把入参给调用/实例化一下,作为依赖注入
- 生成器作为依赖,
FastAPI有隐藏逻辑,会自动调用生成器,并获取返回值,在请求结束后,自动调用生成器的close方法。
其实这就是资源管理器的模式,FastAPI自动调用生成器的close()方法,来释放资源。参考Python
from fastapi import FastAPI, Depends
app = FastAPI()
# 普通依赖:直接调用
def get_settings():
return {"debug": True}
# 生成器依赖:特殊处理
def get_db():
db = create_session()
try:
yield db
finally:
db.close()
@app.get("/users")
def get_users(
settings: dict = Depends(get_settings), # 直接调用 get_settings()
db = Depends(get_db) # 特殊处理 get_db()
):
return db.query("users")
# FastAPI 内部处理流程:
#
# 对于 get_settings(普通依赖):
# result = get_settings() # 直接调用
#
# 对于 get_db(生成器依赖):
# gen = get_db() # 1. 获取生成器
# db = next(gen) # 2. 执行到 yield,获取值
#
# 请求处理...
#
# gen.close() # 3. 请求结束后,清理资源
也就是说,对于地生成器,现在就有了两种取值方式,一种是迭代,一种就是FastAPI这种处理:
# 模式1:迭代器模式(传统用法)
def count_up_to(n):
for i in range(n):
yield i
for num in count_up_to(3):
print(num) # 0, 1, 2
# 模式2:资源管理模式(FastAPI 依赖注入)
def get_resource():
resource = acquire_resource()
try:
yield resource # 只 yield 一次
finally:
release_resource(resource)
# FastAPI 使用这种模式
显式调用close()方法
上面的例子我们提到,FastAPI会自动调用生成器的close()方法,而用依赖注入的生成器,其实就是只有一个yield,那么我们想要清理资源的时候,用next()方法来触发StopIteration异常,在接这个异常的地方触发close()方法不行吗?
不行
- 首先,
close()确实会抛出GeneratorExit异常,但它是生成器内部的,也就是说,你在生成器内部捕获这个异常,或者在finally里,都能在GeneratorExit后按计划执行清理逻辑。 - 但是
next()却是外部异常,也是说,生成器内部捕获不到的,直接raise到外部了,需要你在调用的地方try-catch。
这当然是Python故意这么设计的,因为两者语义不一样:
close()是你主动调用,反而是你在通知生成器,可以结束了,所以生成器用自己的逻辑来让代码走到执行清理那一步(即抛个GeneratorExit异常自己捕获)。next()的语义是获取下一个值,你希望的是生成器继续执行,所以是生成器在通知你,可以结束了,所以生成器抛个StopIteration出来,你捕获这个异常。