關於WSGI
WSGI(全稱Web Server Gateway Interface
),是為 Python 語言定義的Web服務器
和Web應用程序
之間的一種簡單而通用的接口
,它封裝了接受HTTP請求
、解析HTTP請求
、發送HTTP
,響應
等等的這些底層的代碼和操作,使開發者可以高效的編寫Web應用。
一個簡單的使用WSGI的App例子:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [b'<h1>Hello, I Am WSGI!</h1>']
environ
: 一個包含全部HTTP請求信息的字典,由WSGI Server
解包HTTP請求生成。start_response
: 一個WSGI Server
提供的函數,調用可以發送響應的狀態碼和HTTP
報文頭, 函數在返回前必須調用一次start_response()
。application()
應當返回一個可以迭代的對象(HTTP正文)。application()
函數由WSGI Server
直接調用和提供參數。- Python內置了一個
WSGIREF
的WSGI Server
,不過性能不是很好,一般只用在開發環境。可以選擇其他的如Gunicorn
。

Flask的上下文對象
Flask有兩種Context
(上下文),分別是
RequestContext
請求上下文Request
請求的對象,封裝了Http請求(environ
)的內容Session
根據請求中的cookie,重新載入該訪問者相關的會話信息。AppContext
程序上下文g
處理請求時用作臨時存儲的對象。每次請求都會重設這個變量current_app
當前激活程序的程序實例
生命周期:
current_app
的生命周期最長,只要當前程序實例還在運行,都不會失效。Request
和g
的生命周期為一次請求期間,當請求處理完成后,生命周期也就完結了Session
就是傳統意義上的session了。只要它還未失效(用戶未關閉瀏覽器、沒有超過設定的失效時間),那么不同的請求會共用同樣的session。
Flask處理流程

第一步:創建上下文
Flask根據WSGI Server封裝的請求等的信息(environ
)新建RequestContext對象
和AppContext對象
# 聲明對象 # LocalStack LocalProxy 都由Werkzeug提供 # 我們不深究他的細節,那又是另外一個故事了,我們只需知道他的作用就行了 # LocalStack 是棧結構,可以將對象推入、彈出 # 也可以快速拿到棧頂對象。當然,所有的修改都只在本線程可見。 _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() # 如果調用一個LocalStack實例, 能返回一個 LocalProxy 對象 # 這個對象始終指向 這個LocalStack實例的棧頂元素。 # 如果棧頂元素不存在,訪問這個 LocalProxy 的時候會拋出 RuntimeError異常 # LocalProxy對象你只需暫時理解為棧里面的元素即可了 current_app = LocalProxy(_find_app) request = LocalProxy(partial(_lookup_req_object, 'request')) session = LocalProxy(partial(_lookup_req_object, 'session')) g = LocalProxy(partial(_lookup_app_object, 'g'))
# RequestContext class RequestContext(object): def __init__(self, app, environ, request=None): self.app = app if request is None: request = app.request_class(environ) self.request = request self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None
#AppContext class AppContext(object): def __init__(self, app): self.app = app self.url_adapter = app.create_url_adapter(None) self.g = app.app_ctx_globals_class() self._refcnt = 0
這里需要注意的是,RequestContext
在初始化的時候,當前Flask的實例作為參數被傳進來。雖然每次的請求處理都會創建一個RequestContext對象,但是每一次傳入的app參數卻是同一個。通過這個機制,可以使得:
由同一個Flask實例所創建的
RequestContext
,其成員變量app都是同一個Flask實例對象 。實現了多個RequestContext
對應同一個current_app
的目的。
第二步:入棧
將RequestContext
對象push進_request_ctx_stack
里面。在這次請求期間,訪問request對象
,session對象
將指向這個棧的棧頂元素
class RequestContext(object): def push(self): .... _app_ctx_stack.push(self) appcontext_pushed.send(self.app)
AppContext對象push進_app_ctx_stack
里面。在這次請求期間,訪問g
對象將指向這個棧的棧頂元素
class AppContext(object): def push(self): .... _request_ctx_stack.push(self)
第三步:請求分發
response = self.full_dispatch_request()
Flask將調用full_dispatch_request
函數進行請求的分發,之所以不用給參數,是因為我們可以通過request
對象獲得這次請求的信息。full_dispatch_request
將根據請求的url找到對應的藍本里面的視圖函數,並生成一個response
對象。注意的是,在請求之外的時間,訪問request對象是無效的,因為request對象依賴請求期間的_request_ctx_stack
棧。
第四步:上下文對象出棧
這次HTTP的響應已經生成了,就不需要兩個上下文對象了。分別將兩個上下文對象出棧,為下一次的HTTP請求做出准備。
第五步:響應WSGI
調用Response對象,向WSGI Server返回其結果作為HTTP正文。Response對象是一個 可調用對象,當調用發生時,將首先執行WSGI服務器傳入的start_response()函數 發送狀態碼和HTTP報文頭。
最后附上Flask處理請求的wsgi_app
函數
# environ: WSGI Server封裝的HTTP請求信息 # start_response: WSGI Server提供的函數,調用可以發送狀態碼和HTTP報文頭 def wsgi_app(self, environ, start_response): # 根據environ創建上下文 ctx = self.request_context(environ) # 把當前的request context,app context綁定到當前的context ctx.push() error = None try: try: #根據請求的URL,分發請求,經過視圖函數處理后返回響應對象 response = self.full_dispatch_request() except Exception as e: error = e response = self.make_response(self.handle_exception(e)) return response(environ, start_response) finally: if self.should_ignore_error(error): error = None # 最后出棧 ctx.auto_pop(error)