fastapi/docs/zh-hant/docs/advanced/advanced-dependencies.md

8.5 KiB
Raw Blame History

進階相依

參數化的相依

到目前為止看到的相依都是固定的函式或類別。

但有些情況下,你可能想要能為相依設定參數,而不必宣告許多不同的函式或類別。

想像我們想要一個相依,用來檢查查詢參數 q 是否包含某些固定內容。

同時我們希望能將那個固定內容參數化。

「callable」的實例

在 Python 中有一種方式可以讓一個類別的實例變成「callable」。

不是類別本身(類別本來就可呼叫),而是該類別的實例。

要做到這點,我們宣告一個 __call__ 方法:

{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}

在這個情境中,FastAPI 會用這個 __call__ 來檢查額外的參數與子相依,並在之後呼叫它,把回傳值傳遞給你的「路徑操作函式」中的參數。

讓實例可參數化

接著,我們可以用 __init__ 來宣告這個實例的參數,用以「參數化」這個相依:

{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}

在這裡,FastAPI 完全不會接觸或在意 __init__,我們會直接在自己的程式碼中使用它。

建立一個實例

我們可以這樣建立該類別的實例:

{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}

如此一來我們就能「參數化」相依,現在它內部含有 "bar",作為屬性 checker.fixed_content

將實例作為相依使用

然後,我們可以在 Depends(checker) 中使用這個 checker,而不是 Depends(FixedContentQueryChecker),因為相依是那個實例 checker,不是類別本身。

當解析相依時,FastAPI 會像這樣呼叫這個 checker

checker(q="somequery")

...並將其回傳值,作為相依的值,以參數 fixed_content_included 傳給我們的「路徑操作函式」:

{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}

/// tip | 提示

這一切現在看起來也許有點牽強,而且目前可能還不太清楚有何用途。

這些範例刻意保持簡單,但展示了整個機制如何運作。

在關於安全性的章節裡,有一些工具函式也是用同樣的方式實作。

如果你理解了以上內容,你其實已經知道那些安全性工具在底層是如何運作的。

///

同時含有 yieldHTTPExceptionexcept 與背景任務的相依

/// warning | 警告

你很可能不需要這些技術細節。

這些細節主要在於:如果你有一個 0.121.0 之前的 FastAPI 應用,並且在使用含有 yield 的相依時遇到問題,會對你有幫助。

///

含有 yield 的相依隨著時間演進,以涵蓋不同的使用情境並修正一些問題。以下是變更摘要。

含有 yieldscope 的相依

在 0.121.0 版中FastAPI 為含有 yield 的相依加入了 Depends(scope="function") 的支援。

使用 Depends(scope="function") 時,yield 之後的結束程式碼會在「路徑操作函式」執行完畢後立刻執行,在回應發送回客戶端之前。

而當使用 Depends(scope="request")(預設值)時,yield 之後的結束程式碼會在回應送出之後才執行。

你可以在文件中閱讀更多:含有 yield 的相依 - 提前結束與 scope

含有 yieldStreamingResponse 的相依,技術細節

在 FastAPI 0.118.0 之前,如果你使用含有 yield 的相依,它會在「路徑操作函式」回傳之後、發送回應之前,執行結束程式碼。

這樣做的用意是避免在等待回應穿越網路時,比必要的時間更久地占用資源。

但這也意味著,如果你回傳的是 StreamingResponse,該含有 yield 的相依的結束程式碼早已執行完畢。

例如,如果你在含有 yield 的相依中使用了一個資料庫 sessionStreamingResponse 在串流資料時將無法使用該 session因為它已在 yield 之後的結束程式碼中被關閉了。

這個行為在 0.118.0 被還原,使得 yield 之後的結束程式碼會在回應送出之後才被執行。

/// info | 資訊

如下所見,這與 0.106.0 之前的行為非常類似,但對一些邊界情況做了多項改進與錯誤修正。

///

需要提早執行結束程式碼的情境

有些特定條件的使用情境,可能會受益於舊行為(在送出回應之前執行含有 yield 的相依的結束程式碼)。

例如,假設你在含有 yield 的相依中只用資料庫 session 來驗證使用者,而這個 session 之後並未在「路徑操作函式」中使用,僅在相依中使用,且回應需要很長時間才會送出,例如一個慢速傳送資料的 StreamingResponse,但它並沒有使用資料庫。

在這種情況下,資料庫 session 會一直被保留到回應傳送完畢為止,但如果你根本不會用到它,就沒有必要一直持有它。

可能會像這樣:

{* ../../docs_src/dependencies/tutorial013_an_py310.py *}

結束程式碼(自動關閉 Session)在:

{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}

...會在回應完成傳送這些慢速資料後才執行:

{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}

但因為 generate_stream() 並未使用資料庫 session實際上不需要在傳送回應時保持 session 開啟。

如果你用的是 SQLModel或 SQLAlchemy且有這種特定情境你可以在不再需要時明確關閉該 session

{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}

如此一來,該 session 就會釋放資料庫連線,讓其他請求可以使用。

如果你有不同的情境,需要從含有 yield 的相依中提早結束,請建立一個 GitHub 討論問題,描述你的具體情境,以及為何提早關閉含有 yield 的相依對你有幫助。

如果有令人信服的案例需要在含有 yield 的相依中提前關閉,我會考慮加入一種新的選項,讓你可以選擇性啟用提前關閉。

含有 yieldexcept 的相依,技術細節

在 FastAPI 0.110.0 之前,如果你使用含有 yield 的相依,並且在該相依中用 except 捕捉到例外,且沒有再次拋出,那個例外會自動被拋出/轉交給任何例外處理器或內部伺服器錯誤處理器。

在 0.110.0 版本中,這被修改以修復沒有處理器(內部伺服器錯誤)而被轉交的例外所造成的未處理記憶體消耗,並使其行為與一般 Python 程式碼一致。

背景任務與含有 yield 的相依,技術細節

在 FastAPI 0.106.0 之前,不可能在 yield 之後拋出例外;含有 yield 的相依的結束程式碼會在回應送出之後才執行,因此例外處理器{.internal-link target=_blank} 早就已經跑完了。

當初這樣設計主要是為了允許在背景任務中使用由相依「yield」出來的同一組物件因為結束程式碼會在背景任務結束後才執行。

在 FastAPI 0.106.0 中,這個行為被修改,目的是在等待回應穿越網路的期間,不要持有資源。

/// tip | 提示

此外,背景任務通常是一組獨立的邏輯,應該用自己的資源(例如自己的資料庫連線)來處理。

這樣你的程式碼通常會更乾淨。

///

如果你先前依賴這種行為,現在應該在背景任務本身裡建立所需資源,並且只使用不依賴含有 yield 的相依之資源的資料。

例如,不要共用同一個資料庫 session而是在背景任務中建立一個新的資料庫 session並用這個新的 session 從資料庫取得物件。接著,在呼叫背景任務函式時,不是傳遞資料庫物件本身,而是傳遞該物件的 ID然後在背景任務函式內再透過這個 ID 取得物件。