FastAPI 帶有 Yield 的依賴項 (Dependencies with yield)

FastAPI 支援使用 yield 關鍵字來定義依賴項。這讓你的依賴項不僅能提供一個值,還能在使用完畢後執行額外的「清理 (Cleanup)」邏輯。

這在需要開啟與關閉資源的情境中非常強大,例如:

  • 資料庫連線 (Database Connections)。
  • 網路會話 (Network Sessions)。
  • 暫存檔案的建立與刪除。

基礎語法與運作原理

當你在依賴函數中使用 yield 而不是 return 時,FastAPI 會將其視為一個兩個階段的操作:

  1. 請求開始前:執行 yield 之前的代碼(設定階段)。
  2. 注入值:傳遞 yield 出去的值給路徑操作函數。
  3. 回應結束後:當回應處理完畢(不論成功或失敗)後,執行 yield 之後的代碼(清理階段)。

實務範例:資料庫連線

async def get_db():
    db = MyDatabaseSession()
    try:
        # 第一階段:開啟資源並傳遞出去
        yield db
    finally:
        # 第二階段:請求結束後,不論成敗都會執行這裡
        db.close()

在路由中使用:

@app.get("/items/{item_id}")
async def read_item(item_id: str, db: Annotated[MyDatabaseSession, Depends(get_db)]):
    return db.get(item_id)

使用 try...finally 確保安全性

在帶有 yield 的依賴項中,強烈建議使用 try...finally 區塊。

這可以確保即使你的路徑操作函數中途拋出異常,或者後續的依賴項出錯,你的清理邏輯(如關閉連線)依然會被執行,避免資源洩漏。

async def get_resource():
    resource = acquire_resource()
    try:
        yield resource
    finally:
        release_resource(resource)

捕捉路徑操作中的異常 (except)

有時候你希望在依賴項中捕捉並處理路由函數產生的 Exception(例如:發生錯誤時回滾資料庫交易)。

FastAPI 會將路由函數中的異常向外傳遞到 yield 所在的位置。因此,你可以像處理一般 Python 代碼一樣使用 except

async def get_db_with_transaction():
    db = MyDatabaseSession()
    try:
        yield db
        # 如果路由函數順利完成,這裡可以執行 commit
        db.commit()
    except Exception:
        # 如果路由函數拋出異常,這裡執行 rollback
        db.rollback()
        raise # 記得重新拋出異常,讓 FastAPI 的處理機制接手
    finally:
        db.close()
如果你在 except 中捕捉了異常,除非你真的想要「吞掉」這個錯誤(通常不建議),否則請務必再次 raise,讓 FastAPI 能夠產生正確的錯誤回應。

子依賴項與 Yield

帶有 yield 的依賴項也可以擁有子依賴項。FastAPI 會確保整個「依賴鏈」的順序:

  1. 設定階段:由底層往頂層執行(從子依賴到主依賴)。
  2. 執行路由函數
  3. 清理階段:由頂層往底層執行(先清理主依賴,再清理子依賴)。

這就像一個「先進後出 (LIFO)」的堆疊結構。

使用限制

  • 不能在 dependencies= 中使用需要回傳值的 yield 依賴項:雖然路徑裝飾器的 dependencies=[Depends(...)] 參數可以使用 yield 依賴項來執行副作用,但由於該方式會丟棄回傳值,因此如果你需要 yield 出來的物件,請務必將其注入到函數參數中。
  • 背景任務 (Background Tasks):如果你使用了 yield 依賴項,請注意該清理邏輯會在「回應已送出」後才執行。但如果在依賴項中關閉了資料庫連線,你的背景任務可能就無法再存取該連線。

總結

  • yield 讓依賴項具備 SetupCleanup 的能力。
  • 使用 try...finally 是最安全的資源釋放方式。
  • 你可以透過 except 捕捉路由層級的異常。
  • 這是管理資料庫連線、檔案控制等資源生命週期的標準做法。