WSGI(全稱Web Server Gateway Interface
),是爲 Python 語言定義的Web服務器
和Web應用程序
之間的一種簡單而通用的接口
,它封裝了接受HTTP請求
、解析HTTP請求
、發送HTTP
,響應
等等的這些底層的代碼和操做,使開發者能夠高效的編寫Web應用。php
一個簡單的使用WSGI的App例子:html
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
直接調用和提供參數。WSGIREF
的WSGI Server
,不過性能不是很好,通常只用在開發環境。能夠選擇其餘的如Gunicorn
。Flask有兩種Context
(上下文),分別是python
RequestContext
請求上下文Request
請求的對象,封裝了Http請求(environ
)的內容Session
根據請求中的cookie,從新載入該訪問者相關的會話信息。AppContext
程序上下文g
處理請求時用做臨時存儲的對象。每次請求都會重設這個變量current_app
當前激活程序的程序實例生命週期:瀏覽器
current_app
的生命週期最長,只要當前程序實例還在運行,都不會失效。Request
和g
的生命週期爲一次請求期間,當請求處理完成後,生命週期也就完結了Session
就是傳統意義上的session了。只要它還未失效(用戶未關閉瀏覽器、沒有超過設定的失效時間),那麼不一樣的請求會共用一樣的session。Flask根據WSGI Server封裝的請求等的信息(environ
)新建RequestContext對象
和AppContext對象
ruby
# 聲明對象 # 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參數倒是同一個。經過這個機制,可使得:bash
由同一個Flask實例所建立的
RequestContext
,其成員變量app都是同一個Flask實例對象 。實現了多個RequestContext
對應同一個current_app
的目的。服務器
將RequestContext
對象push進_request_ctx_stack
裏面。在此次請求期間,訪問request對象
,session對象
將指向這個棧的棧頂元素cookie
class RequestContext(object): def push(self): .... _app_ctx_stack.push(self) appcontext_pushed.send(self.app)
AppContext對象push進_app_ctx_stack
裏面。在此次請求期間,訪問g
對象將指向這個棧的棧頂元素session
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
棧。app
此次HTTP的響應已經生成了,就不須要兩個上下文對象了。分別將兩個上下文對象出棧,爲下一次的HTTP請求作出準備。
調用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)