從上圖可以看出來,wsgi基本處理模式爲 : WSGI Server -> (WSGI Middleware)* -> WSGI Application 。
- 服務器創建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信息塞回給客戶端。
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')]
我們再用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 class AppClass: 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
from wsgiref.simple_server import make_server URL_PATTERNS= ( ('hi/','say_hi'), ('hello/','say_hello'), ) class Dispatcher(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:
class Auth(object): def __init__(self,app): self.app = app def __call__(self,environ, start_response): #TODO return self.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): return ErrorHandlerMiddleware( SessionMiddleware( IdentificationMiddleware( AuthenticationMiddleware( UrlParserMiddleware(app))))))
只要這些Middleware符合wsgi規範,甚至還可以在各個框架之間組合重用。例如pylons的認證Middleware可以直接被TurboGears拿去使用。
好了,各個部分都寫完了,以後有時間再看看pylons的代碼,相信又會對wsgi有很多新的理解。