WSGI的理解(轉載)
阿新 • • 發佈:2019-01-25
wsgi是一個搞web開發的pythoner必須瞭解的內容,之前也零散的看過一些文章,但總感覺好多概念很模糊。這幾天抽空又把相關內容好好整理了一下,把筆記貼出來,一些隻言片語也許對某些正在研究這個的人有所幫助。
wsgi 是一個 web 元件的介面規範.,wsgi將 web 元件分為三類: web伺服器,web中介軟體,web應用程式,
通過這個圖可以看出來wsgi server 基本工作流程
從上圖可以看出來,wsgi基本處理模式為 : WSGI Server -> (WSGI Middleware)* -> WSGI Application 。
下面分別來看這三個元件 WSGI Server/gateway wsgi server可以理解為一個符合wsgi規範的web server,接收request請求,封裝一系列環境變數,按照wsgi規範呼叫註冊的wsgi app,最後將response返回給客戶端。文字很難解釋清楚wsgi server到底是什麼東西,以及做些什麼事情,最直觀的方式還是看wsgi server的實現程式碼。以python自帶的wsgiref為例,wsgiref是按照wsgi規範實現的一個簡單wsgi server。它的程式碼也不復雜,下圖是我讀wsgiref程式碼後整理的。- 伺服器建立socket,監聽埠,等待客戶端連線。
- 當有請求來時,伺服器解析客戶端資訊放到環境變數environ中,並呼叫繫結的handler來處理請求。
- handler解析這個http請求,將請求資訊例如method,path等放到environ中。
- wsgi handler再將一些伺服器端資訊也放到environ中,最後伺服器資訊,客戶端資訊,本次請求資訊全部都儲存到了環境變數environ中。
- wsgi handler 呼叫註冊的wsgi app,並將environ和回撥函式傳給wsgi app
- wsgi app 將reponse header/status/body 回傳給wsgi handler
- 最終handler還是通過socket將response資訊塞回給客戶端。
defsimple_app(environ, start_response): status ='200 OK' response_headers =[('Content-type','text/plain')] start_response(status, response_headers)return[u"This is hello wsgi app".encode('utf8')]
我們再用wsgiref 作為wsgi server ,然後呼叫這個wsgi app,就能直觀看到一次request,response的效果,簡單修改程式碼如下:
from wsgiref.simple_server import make_server def simple_app(environ, start_response): status ='200 OK' response_headers =[('Content-type','text/plain')] start_response(status, response_headers)return[u"This is hello wsgi app".encode('utf8')] httpd = make_server('',8000, simple_app)print"Serving on port 8000..." httpd.serve_forever()
訪問http://127.0.0.1:8000 就能看到效果了。
此外,上面講到了wsgi app只要是一個callable物件就可以了,因此不一定要是函式,一個實現了__call__方法的例項也可以,示例程式碼如下:
from wsgiref.simple_server import make_server classAppClass:def __call__(self,environ, start_response): status ='200 OK' response_headers =[('Content-type','text/plain')] start_response(status, response_headers)return["hello world!"] app =AppClass() httpd = make_server('',8000, app)print"Serving on port 8000..." httpd.serve_forever()
WSGI MiddleWare
上面的application看起來沒什麼意思,感覺沒有太大用,但加上一層層的middleware包裝之後就不一樣了。一堆文字解釋可能還沒有一個demo更容易說明白,我寫了一個簡單Dispatcher Middleware,用來實現URL 路由:from wsgiref.simple_server import make_server URL_PATTERNS=(('hi/','say_hi'),('hello/','say_hello'),)classDispatcher(object):def _match(self,path): path = path.split('/')[1]for url,app in URL_PATTERNS:if path in url:return app def __call__(self,environ, start_response): path = environ.get('PATH_INFO','/') app =self._match(path)if app : app = globals()[app]return app(environ, start_response)else: start_response("404 NOT FOUND",[('Content-type','text/plain')])return["Page dose not exists!"]def say_hi(environ, start_response): start_response("200 OK",[('Content-type','text/html')])return["kenshin say hi to you!"]def say_hello(environ, start_response): start_response("200 OK",[('Content-type','text/html')])return["kenshin say hello to you!"] app =Dispatcher() httpd = make_server('',8000, app)print"Serving on port 8000..." httpd.serve_forever()
上面的例子可以看出來,middleware 包裝之後,一個簡單wsgi app就有了URL dispatch功能。然後我還可以在這個app外面再加上其它的middleware來包裝它,例如加一個許可權認證的middleware:
classAuth(object):def __init__(self,app):self.app = app def __call__(self,environ, start_response):#TODOreturnself.app(environ, start_response) app =Dispatcher() auth_app =Auth(app) httpd = make_server('',8000, auth_app)print"Serving on port 8000..." httpd.serve_forever()
經過這些middleware的包裝,已經有點框架的感覺了。其實基於wsgi的框架,例如paste,pylons就是這樣通過一層層middleware組合起來的。只是一個成熟的框架,這樣的middleware會有很多,例如:
def configure(app):returnErrorHandlerMiddleware(SessionMiddleware(IdentificationMiddleware(AuthenticationMiddleware(UrlParserMiddleware(app))))))
只要這些Middleware符合wsgi規範,甚至還可以在各個框架之間組合重用。例如pylons的認證Middleware可以直接被TurboGears拿去使用。
好了,各個部分都寫完了,以後有時間再看看pylons的程式碼,相信又會對wsgi有很多新的理解。