本文介紹了 WSGI 是什麼以及 WSGI 的處理流程。python
服務器網關接口(WSGI)是 web 服務器和 web 應用(或 Python 框架)之間的接口規範,旨在提升 web 應用在 web 服務器上的可遷移性。若是一個應用遵循了 WSGI 規範,那麼這個應用能夠運行在任何遵循 WSGI 規範的服務器上。web
服務器指任何實現了 WSGI 規範的服務器,好比 Gunicorn、uWSGI。服務器
WSGI 有兩端:app
服務器調用一個由應用端提供的可調用對象。由服務器定義客戶端應該如何提供這個對象。好比一些服務器須要應用的部署者去寫一個簡短的腳原本建立一個服務器的實例,並向服務器實例提供應用對象。另外一些服務器可能使用配置文件或者其餘機制來指定應用對象應該從哪裏導入。框架
除了單純的服務器和應用端,也能夠建立一箇中間件來實現兩邊的定義。這樣的組件對於服務器來講是應用端,而對於應用端來講是服務器,而且能夠被用來提供擴展 APIs、內容轉換、導航和其餘功能。函數
可調用對象能夠指一個函數、方法、類或者一個擁有 __call__
方法的實例。由服務器或實現其的應用端來選擇針對他們需求的合適技術。相反的,一個執行可調用對象的服務器或者客戶端對提供給它們的可調用對象不能有任何假設。可調用對象只是被用來調用,不須要檢查它。ui
以請求數據組成的字典
和處理函數
的應用可調用對象若是有中間件,那麼中間件位於服務器和應用之間,中間件相對於服務器是應用,相對於應用是服務器。this
WSGI 定義了兩種字符串:.net
str
)bytes
實現,Python 2 使用 str
),用於請求和響應的 body(好比 POST/PUT 的輸入數據和 HTML 輸出)['str1', 'str2', ]
)做爲響應體應用對象必須能夠被執行屢次,實際上全部的服務器(除了 CGI) 都會發送重複的請求。code
每當服務器收到一個 HTTP 客戶端的請求,服務器會執行應用的可調用對象。獲取結果後,將其發送給客戶端。
定義可調用應用對象,並啓動 WSGI 服務器:
#! /usr/bin/env python # Python's bundled WSGI server from wsgiref.simple_server import make_server def application(environ, start_response): # Sorting and stringifying the environment key, value pairs response_body = [ '%s: %s' % (key, value) for key, value in sorted(environ.items()) ] response_body = '\n'.join(response_body) # Response body must be bytes response_body = response_body.encode() status = '200 OK' response_headers = [ ('Content-Type', 'text/plain'), ('Content-Length', str(len(response_body))) ] start_response(status, response_headers) return [response_body] # Instantiate the server httpd = make_server( 'localhost', # The host name 8051, # A port number where to wait for the request application # The application object name, in this case a function ) # Wait for a single request, serve it and quit httpd.handle_request()
以上代碼來自 http://wsgi.tutorial.codepoint.net/environment-dictionary
啓動 WSGI 服務後,訪問 http://localhost:8051/ 可得到響應。
服務器調用應用獲得響應後,將其發送給客戶端
# 來自 wsgiref.handlers.BaseHandler#finish_response def finish_response(self): try: if not self.result_is_file() or not self.sendfile(): for data in self.result: self.write(data) self.finish_content() finally: self.close()