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出来,你捕获这个异常。