WSGI到底是什麼?
在用Python Web開發時經常會遇到WSGI,所以WSGI到底是什麼呢?本文我們一起來揭開WSGI神祕的面紗!
先來看一下WSGI的介紹:
全稱Python Web Server Gateway Interface,指定了web伺服器和Python web應用或web框架之間的標準介面,以提高web應用在一系列web伺服器間的移植性。 具體可檢視 官方文件
從以上介紹我們可以看出:
- WSGI是一套介面標準協議/規範;
- 通訊(作用)區間是Web伺服器和Python Web應用程式之間;
- 目的是制定標準,以保證不同Web伺服器可以和不同的Python程式之間相互通訊
你可能會問,為什麼需要WSGI?
首先,我們明確一下web應用處理請求的具體流程:
- 使用者操作操作瀏覽器傳送請求;
- 請求轉發至對應的web伺服器
- web伺服器將請求轉交給web應用程式,web應用程式處理請求
- web應用將請求結果返回給web伺服器,由web伺服器返回使用者響應結果
- 瀏覽器收到響應,向用戶展示
可以看到,請求時Web伺服器需要和web應用程式進行通訊,但是web伺服器有很多種啊,Python web應用開發框架也對應多種啊,所以WSGI應運而生,定義了一套通訊標準。試想一下,如果不統一標準的話,就會存在Web框架和Web伺服器資料無法匹配的情況,那麼開發就會受到限制,這顯然不合理的。
既然定義了標準,那麼WSGI的標準或規範是?
web伺服器在將請求轉交給web應用程式之前,需要先將http報文轉換為WSGI規定的格式。
WSGI規定,Web程式必須有一個可呼叫物件,且該可呼叫物件接收兩個引數,返回一個可迭代物件:
- environ:字典,包含請求的所有資訊
- start_response:在可呼叫物件中呼叫的函式,用來發起響應,引數包括狀態碼,headers等
通過以上學習,一起實現一個簡單WSGI服務吧
首先,我們編寫一個符合WSGI標準的一個http處理函式:
def hello(environ, start_response): status = "200 OK" response_headers = [('Content-Type', 'text/html')] start_response(status, response_headers) path = environ['PATH_INFO'][1:] or 'hello' return [b'<h1> %s </h1>' % path.encode()]
該方法負責獲取environ字典中的path_info,也就是獲取請求路徑,然後在前端展示。
接下來,我們需要一個伺服器啟動WSGI伺服器用來處理驗證,使用Python內建的WSGI伺服器模組wsgiref,編寫server.py:
# coding:utf-8
"""
desc: WSGI伺服器實現
"""
from wsgiref.simple_server import make_server
from learn_wsgi.client import hello
def main():
server = make_server('localhost', 8001, hello)
print('Serving HTTP on port 8001...')
server.serve_forever()
if __name__ == '__main__':
main()
執行python server.py,瀏覽器開啟"http://localhost:8001/a",即可驗證。
通過實現一個簡單的WSGI服務,我們可以看到:通過environ可以獲取http請求的所有資訊,http響應的資料都可以通過start_response加上函式的返回值作為body。
當然,以上只是一個簡單的案例,那麼在python的Web框架內部是如何遵循WSGI規範的呢?以Flask舉例,
Flask與WSGI
Flask中的程式例項app就是一個可呼叫物件,我們建立app例項時所呼叫的Flask類實現了__call__方法,__call__方法呼叫了wsgi_app()方法,該方法完成了請求和響應的處理,WSGI伺服器通過呼叫該方法傳入請求資料,獲取返回資料:
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
Flask的werkzeug庫是一個非常優秀的WSGI工具庫,具體的實現我們之後再詳細學習。
以上