每篇前言:
????作者介紹:【孤寒者】—CSDN全棧領(lǐng)域優(yōu)質(zhì)創(chuàng)作者、HDZ核心組成員、華為云享專家Python全棧領(lǐng)域博主、CSDN原力計劃作者
- ????本文已收錄于Flask框架從入門到實戰(zhàn)專欄:《Flask框架從入門到實戰(zhàn)》
- ????熱門專欄推薦:《Python全棧系列教程》、《Django框架從入門到實戰(zhàn)》、《爬蟲從入門到精通系列教程》、《前端系列教程》、《tornado一條龍+一個完整版項目》。
- ?????本專欄面向廣大程序猿,為的是大家都做到Flask從入門到精通,穿插有很多實戰(zhàn)優(yōu)化點。
- ????訂閱專欄后可私聊進一千多人Python全棧交流群(手把手教學(xué),問題解答); 進群可領(lǐng)取Python全棧教程視頻 + 多得數(shù)不過來的計算機書籍:基礎(chǔ)、Web、爬蟲、數(shù)據(jù)分析、可視化、機器學(xué)習(xí)、深度學(xué)習(xí)、人工智能、算法、面試題等。
- ????加入我一起學(xué)習(xí)進步,一個人可以走的很快,一群人才能走的更遠!
![]()
引子:
當(dāng)一個客戶端,比如瀏覽器,向 Flask 服務(wù)發(fā)起 HTTP 請求,它首先會被 Web 服務(wù)器(如 gunicorn 或 uWSGI)接收。這個 Web 服務(wù)器的任務(wù)不僅僅是接收請求,它還作為 Flask 應(yīng)用程序與外部環(huán)境之間的橋梁。
為了使 Web 服務(wù)器和 Python Web 應(yīng)用程序能夠“對話”,我們需要一個規(guī)范或者說是一個協(xié)議。這就是 WSGI,即 Web Server Gateway Interface。Flask 利用 Werkzeug 這一 WSGI 工具庫來滿足這個規(guī)范的需求。
現(xiàn)在,當(dāng)一個 HTTP 請求達到 Web 服務(wù)器,Werkzeug 會介入并起到關(guān)鍵作用。它的職責(zé)是從原始 HTTP 請求中提取出有意義的數(shù)據(jù),并將其轉(zhuǎn)化為 Flask 可以輕松操作的格式。這意味著,原始的請求數(shù)據(jù)如:
GET /index.html HTTP/1.1
Host: www.example.com
會被 Werkzeug 解析,并且轉(zhuǎn)化為 Flask 可以直接使用的請求對象,比如 flask.request
。這樣,開發(fā)者可以輕松地訪問請求的各個部分,例如 headers、query parameters、body 等,無需深入了解底層的 HTTP 協(xié)議。
簡而言之,通過 WSGI 和 Werkzeug 的配合,當(dāng) HTTP 請求達到 Flask 應(yīng)用時,我們可以直觀、高效地處理它,使開發(fā)變得更為簡潔。
看源碼捋一下一個完整請求在Flask里整個生命周期都干了啥?
前面講過請求一旦到來,就會執(zhí)行app.__call__
方法:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world'
if __name__ == '__main__':
app.__call__
app.run()
進入__call__
方法:
【需要注意的是:參數(shù)environ
已經(jīng)是一個經(jīng)過 WSGI 服務(wù)器處理后的字典,它包含了所有與 HTTP 請求相關(guān)的信息。即當(dāng)一個 HTTP 請求到達 WSGI 服務(wù)器時,服務(wù)器會解析這個請求,并將相關(guān)的信息轉(zhuǎn)化為 environ
字典中的一系列鍵值對!】
(拓展:start_response
是 WSGI 規(guī)范中定義的一個回調(diào)函數(shù),它的主要作用是設(shè)置響應(yīng)的狀態(tài)和 HTTP 頭部。當(dāng)你的應(yīng)用程序決定如何響應(yīng)請求時,它需要調(diào)用這個函數(shù)來開始發(fā)送響應(yīng))
繼續(xù)進去:
上面選中那一句實現(xiàn)了三個功能:
- 將WSGI處理之后的請求數(shù)據(jù)environ又處理了一遍;
- 設(shè)置session為None;
- 實現(xiàn)路由匹配(根據(jù)url找到對應(yīng)視圖函數(shù))。
下面扣源碼來證實~
進去request_context
:
繼續(xù)進去:
- 下圖第一個箭頭所指位置就是功能一:將WSGI處理之后的請求數(shù)據(jù)environ又處理了一遍。主要任務(wù)就是將 WSGI 提供的
environ
字典轉(zhuǎn)換為 Flask 可以更容易處理的請求對象(可以自己繼續(xù)進源碼去探究) - 下圖第二個箭頭所指位置就是功能二:設(shè)置session為None。
而上圖最后一個箭頭所指位置就是功能三:實現(xiàn)路由匹配(后續(xù)深入講解)~
回到wsgi_app
函數(shù),繼續(xù)下一句:
進去:
其他部分先不管,直接看上圖箭頭所指位置,是不是很眼熟?
- 這部分就是Flask使用了自己實現(xiàn)的threading.local()對象!
進去_request_ctx_stack
對象:
進去LocalStack()
對象:
回退兩層:
下圖箭頭所指就是給ctx里的session賦值:
- 下圖倒數(shù)第三行調(diào)用了應(yīng)用對象(通常是一個Flask應(yīng)用實例)的
open_session
方法,并傳入當(dāng)前的請求對象self.request
。這個方法的目標(biāo)是從請求中提取會話數(shù)據(jù)(如果存在的話)并返回一個會話對象。 - 如果
open_session
沒有返回一個有效的會話對象(例如,當(dāng)前請求可能是一個全新的請求,沒有任何之前的會話數(shù)據(jù)),那么self.session
將為None
。在這種情況下,代碼會調(diào)用make_null_session
方法來創(chuàng)建一個新的、空的會話對象。這確保了self.session
始終有一個有效的會話對象,無論是從請求中提取的還是新創(chuàng)建的空會話。
回去繼續(xù)往下扒:
下圖箭頭所指就是Flask用于處理一個請求并返回相應(yīng)響應(yīng)的核心邏輯:
- 當(dāng)一個HTTP請求到達Flask應(yīng)用時,它需要經(jīng)過一系列的處理步驟,如:預(yù)處理(前置處理),路由匹配,視圖函數(shù)處理,以及后處理(后置處理)等,然后最終得到一個HTTP響應(yīng)(response)。
full_dispatch_request
方法封裝了這整個流程。
進去:
簡單講一下這個函數(shù)各語句功能:
觸發(fā)首次請求前的函數(shù):
self.try_trigger_before_first_request_functions()
這一行嘗試觸發(fā)任何注冊為“在第一個請求之前執(zhí)行”的函數(shù)。這些函數(shù)只會在應(yīng)用收到其第一個請求時執(zhí)行一次,通常用于一些應(yīng)用的初始化工作。
發(fā)送請求開始信號:
request_started.send(self)
這一行發(fā)送一個
request_started
信號。Flask使用信號來允許開發(fā)者在某些事件(如請求開始或結(jié)束)發(fā)生時執(zhí)行自定義代碼。請求預(yù)處理:
rv = self.preprocess_request()
這一行調(diào)用
preprocess_request
方法來執(zhí)行任何注冊的請求預(yù)處理函數(shù),例如before_request
鉤子。這些鉤子可以用于各種目的,如用戶身份驗證、設(shè)置數(shù)據(jù)庫連接等。請求分發(fā):
if rv is None: rv = self.dispatch_request()
如果預(yù)處理函數(shù)沒有返回任何值(即返回
None
),則該代碼調(diào)用dispatch_request
方法。dispatch_request
方法的職責(zé)是根據(jù)當(dāng)前請求的URL找到對應(yīng)的路由和視圖函數(shù),并執(zhí)行它。請求后處理:
return self.finalize_request(rv)
最后,無論請求處理過程中是否發(fā)生異常,
finalize_request
方法都會被調(diào)用。它負責(zé)執(zhí)行任何注冊的請求后處理函數(shù)(例如after_request
鉤子)并返回最終的HTTP響應(yīng)。綜上所述,
full_dispatch_request
方法封裝了Flask處理HTTP請求的整個流程,包括前后處理、路由分發(fā)、異常處理和最終響應(yīng)的創(chuàng)建。
先進去preprocess_request()
函數(shù):
【可以看到確實在執(zhí)行所有注冊的請求預(yù)處理函數(shù),例如before_request
鉤子】
回去一層,進去finalize_request()
函數(shù):
繼續(xù)進去:
繼續(xù)進去:
重要部分:
-
self.session_interface
是Flask應(yīng)用中用于處理會話的接口。is_null_session
方法檢查給定的會話(在這里是ctx.session
)是否是一個空會話。如果不是一個空會話(也就是說,會話中包含了一些數(shù)據(jù)),那么條件判斷為真。 - 當(dāng)條件判斷為真時,
save_session
方法將當(dāng)前的會話數(shù)據(jù)(ctx.session
)保存到響應(yīng)中(response
)。這通常涉及將會話數(shù)據(jù)加密并設(shè)置為一個Cookie,然后將該Cookie附加到HTTP響應(yīng)中。這樣,當(dāng)瀏覽器接收到這個響應(yīng)時,它會保存這個Cookie,并在后續(xù)的請求中將其發(fā)送回服務(wù)器。這使得服務(wù)器能夠識別并“記住”用戶之間的連續(xù)請求。
回到最開始:
繼續(xù)進去:
繼續(xù)進去:
看看_request_ctx_stack
對象是啥呢?
進去:
【threading.local()對象???:是的!】
文章來源:http://www.zghlxwxcb.cn/news/detail-805531.html
文末扯幾句:
本文較為粗糙地捋了一遍Flask最為核心部分(上下文管理)的源碼!
粗糙是粗糙,但味道沒有錯,仔細扣扣源碼~
后續(xù)幾篇文章會繼續(xù)細化這部分!文章來源地址http://www.zghlxwxcb.cn/news/detail-805531.html
到了這里,關(guān)于(二十)Flask之上下文管理第一篇(粗糙縷一遍源碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!