應用程式結構與生命週期

Flask 讓編寫 Web 應用程式變得相當容易。但是一個應用程式及其處理的每個請求都包含許多不同的部分。了解應用程式設定、伺服和處理請求期間發生的事情,將有助於您了解 Flask 中可能實現的功能以及如何建構您的應用程式。

應用程式設定

建立 Flask 應用程式的第一步是建立應用程式物件。每個 Flask 應用程式都是 Flask 類別的實例,該類別收集所有組態、擴充功能和視圖。

from flask import Flask

app = Flask(__name__)
app.config.from_mapping(
    SECRET_KEY="dev",
)
app.config.from_prefixed_env()

@app.route("/")
def index():
    return "Hello, World!"

這被稱為「應用程式設定階段」,它是您在任何視圖函式或其他處理常式之外編寫的程式碼。它可以分散在不同的模組和子套件中,但您希望成為應用程式一部分的所有程式碼都必須匯入才能註冊。

所有應用程式設定都必須在您開始伺服應用程式和處理請求之前完成。這是因為 WSGI 伺服器將工作分配到多個工作程序之間,或者可以分佈在多部機器上。如果組態在一個工作程序中變更,Flask 無法確保其他工作程序之間的一致性。

Flask 嘗試透過在請求處理後呼叫與設定相關的方法時顯示錯誤,來協助開發人員捕捉到其中一些設定順序問題。在這種情況下,您會看到此錯誤

設定方法 'route' 無法再在應用程式上呼叫。它已經處理了它的第一個請求,任何變更將不會一致地應用。請確保在執行應用程式之前完成設定應用程式所需的所有匯入、裝飾器、函式等。

但是,Flask 無法偵測所有設定順序錯誤的情況。一般來說,請勿在請求期間執行的視圖函式中修改 Flask 應用程式物件和 Blueprint 物件。這包括

  • 使用 @app.route@app.errorhandler@app.before_request 等新增路由、視圖函式和其他請求處理常式。

  • 註冊藍圖。

  • 使用 app.config 載入組態。

  • 使用 app.jinja_env 設定 Jinja 模板環境。

  • 設定 Session 介面,而不是預設的 itsdangerous Cookie。

  • 使用 app.json 設定 JSON 提供者,而不是預設提供者。

  • 建立和初始化 Flask 擴充功能。

伺服應用程式

Flask 是一個 WSGI 應用程式框架。WSGI 的另一半是 WSGI 伺服器。在開發期間,Flask 透過 Werkzeug 提供了一個開發 WSGI 伺服器,搭配 flask run CLI 命令。當您完成開發後,請使用生產伺服器來伺服您的應用程式,請參閱 部署到生產環境

無論您使用哪個伺服器,它都將遵循 PEP 3333 WSGI 規範。WSGI 伺服器將被告知如何存取您的 Flask 應用程式物件,即 WSGI 應用程式。然後它將開始監聽 HTTP 請求,將請求資料轉換為 WSGI environ,並使用該資料呼叫 WSGI 應用程式。WSGI 應用程式將傳回轉換為 HTTP 回應的資料。

  1. 瀏覽器或其他用戶端發出 HTTP 請求。

  2. WSGI 伺服器接收請求。

  3. WSGI 伺服器將 HTTP 資料轉換為 WSGI environ 字典。

  4. WSGI 伺服器使用 environ 呼叫 WSGI 應用程式。

  5. Flask,WSGI 應用程式,執行其所有內部處理,以將請求路由到視圖函式、處理錯誤等。

  6. Flask 將視圖函式傳回值轉換為 WSGI 回應資料,並將其傳遞給 WSGI 伺服器。

  7. WSGI 伺服器建立並傳送 HTTP 回應。

  8. 用戶端接收 HTTP 回應。

中介軟體

上面的 WSGI 應用程式是一個以特定方式運作的可呼叫物件。中介軟體是一個封裝另一個 WSGI 應用程式的 WSGI 應用程式。它與 Python 裝飾器的概念類似。最外層的中介軟體將由伺服器呼叫。它可以修改傳遞給它的資料,然後呼叫它封裝的 WSGI 應用程式(或更進一步的中介軟體),依此類推。它可以取得該呼叫的傳回值並進一步修改它。

從 WSGI 伺服器的角度來看,只有一個 WSGI 應用程式,即它直接呼叫的那個。通常,Flask 是中介軟體鏈末端的「真實」應用程式。但即使 Flask 也可以呼叫更進一步的 WSGI 應用程式,儘管這是一種進階、不常見的用例。

您會看到與 Flask 一起使用的常見中介軟體是 Werkzeug 的 ProxyFix,即使請求透過 HTTP Proxy 傳輸,它也會修改請求,使其看起來像是直接來自用戶端。還有其他中介軟體可以處理伺服靜態檔案、身份驗證等。

如何處理請求

對於我們來說,上述步驟中有趣的部分是 Flask 何時被 WSGI 伺服器(或中介軟體)呼叫。在那個時候,它將做很多事情來處理請求並產生回應。最基本的是,它會將 URL 與視圖函式匹配,呼叫視圖函式,並將傳回值傳遞回伺服器。但是還有許多其他部分,您可以使用它們來自訂其行為。

  1. WSGI 伺服器呼叫 Flask 物件,該物件呼叫 Flask.wsgi_app()

  2. 建立 RequestContext 物件。這會將 WSGI environ 字典轉換為 Request 物件。它還會建立一個 AppContext 物件。

  3. 應用程式上下文被推送,這使得 current_appg 可用。

  4. appcontext_pushed 信號被發送。

  5. 請求上下文被推送,這使得 requestsession 可用。

  6. Session 被打開,使用應用程式的 session_interfaceSessionInterface 的實例)載入任何現有的 Session 資料。

  7. URL 會與應用程式設定期間使用 route() 裝飾器註冊的 URL 規則進行匹配。如果沒有匹配項,則錯誤(通常是 404、405 或重定向)會被儲存以供稍後處理。

  8. request_started 信號被發送。

  9. 任何 url_value_preprocessor() 裝飾的函式都會被呼叫。

  10. 任何 before_request() 裝飾的函式都會被呼叫。如果這些函式中的任何一個傳回值,則該值會立即被視為回應。

  11. 如果 URL 在前幾個步驟中沒有與路由匹配,則現在會引發該錯誤。

  12. 與匹配的 URL 關聯的 route() 裝飾的視圖函式被呼叫,並傳回一個值,該值將用作回應。

  13. 如果到目前為止的任何步驟引發了例外,並且有一個 errorhandler() 裝飾的函式與例外類別或 HTTP 錯誤代碼匹配,則會呼叫它來處理錯誤並傳回回應。

  14. 無論是 before request 函式、視圖還是錯誤處理常式傳回了回應值,該值都會被轉換為 Response 物件。

  15. 任何 after_this_request() 裝飾的函式都會被呼叫,然後被清除。

  16. 任何 after_request() 裝飾的函式都會被呼叫,它們可以修改回應物件。

  17. Session 被儲存,使用應用程式的 session_interface 持續保存任何修改過的 Session 資料。

  18. request_finished 信號被發送。

  19. 如果到目前為止的任何步驟引發了例外,並且它沒有被錯誤處理常式函式處理,則現在會處理它。HTTP 例外被視為具有其對應狀態碼的回應,其他例外則被轉換為一般 500 回應。got_request_exception 信號被發送。

  20. 回應物件的狀態、標頭和主體被傳回給 WSGI 伺服器。

  21. 任何 teardown_request() 裝飾的函式都會被呼叫。

  22. request_tearing_down 信號被發送。

  23. 請求上下文被彈出,requestsession 不再可用。

  24. 任何 teardown_appcontext() 裝飾的函式都會被呼叫。

  25. appcontext_tearing_down 信號被發送。

  26. 應用程式上下文被彈出,current_appg 不再可用。

  27. appcontext_popped 信號被發送。

還有比這更多的裝飾器和自訂點,但它們並非每個請求生命週期的一部分。它們更特定於您在請求期間可能使用的某些內容,例如模板、建構 URL 或處理 JSON 資料。請參閱本文檔的其餘部分以及 API 以進一步探索。