本文來自於千鋒教育在阿里雲開發者社區學習中心上線課程《Python入門2020最新大課》,主講人姜偉。
WSGI服務器的介紹
WSGI接口定義非常簡單,它只要求Web開發者實現一個函數,就可以響應HTTP請求。我們來看一個最簡單的Web版本的“Hello, web!”:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return '<h1>Hello, web!</h1>'
上面的application()函數就是符合WSGI標準的一個HTTP處理函數,它接收兩個參數:
- environ:一個包含所有HTTP請求信息的dict對象;
- start_response:一個發送HTTP響應的函數。
在application()函數中,調用:
start_response('200 OK', [('Content-Type', 'text/html')])
就發送了HTTP響應的Header,注意Header只能發送一次,也就是隻能調用一次start_response()函數。start_response()函數接收兩個參數,一個是HTTP響應碼,一個是一組list表示的HTTP Header,每個Header用一個包含兩個str的tuple表示。
通常情況下,都應該把Content-Type頭髮送給瀏覽器。其他很多常用的HTTP Header也應該發送。
然後,函數的返回值'<h1>Hello, web!</h1>'
將作為HTTP響應的Body發送給瀏覽器。
有了WSGI,我們關心的就是如何從environ這個dict對象拿到HTTP請求信息,然後構造HTML,通過start_response()發送Header,最後返回Body。
整個application()函數本身沒有涉及到任何解析HTTP的部分,也就是說,底層代碼不需要我們自己編寫,我們只負責在更高層次上考慮如何響應請求就可以了。
application()函數怎麼調用?如果我們自己調用,兩個參數environ和start_response我們沒法提供,返回的str也沒法發給瀏覽器。
所以application()函數必須由WSGI服務器來調用。有很多符合WSGI規範的服務器,我們可以挑選一個來用。但是現在,我們只想儘快測試一下我們編寫的application()函數真的可以把HTML輸出到瀏覽器,所以,要趕緊找一個最簡單的WSGI服務器,把我們的Web應用程序跑起來。
而Python內置了一個WSGI服務器,這個模塊叫wsgiref,它是用純Python編寫的WSGI服務器的參考實現。所謂“參考實現”是指該實現完全符合WSGI標準,但是不考慮任何運行效率,僅供開發和測試使用。
示例:
from wsgiref.simple_server import make_server
# demo_app 需要兩個參數
# 第 0 個參數,表示環境(電腦的環境;請求路徑相關的環境)
# 第 1 個參數,是一個函數,用來返回響應頭
# 這個函數需要一個返回值,返回值是一個列表
# 列表裡只有一個元素,是一個二進制,表示返回給瀏覽器的數據
def demo_app(environ, start_response):
# environ是一個字典,保存了很多的數據
# 其中重要的一個是 PATH_INFO能夠獲取到用戶的訪問路徑
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8')])
return ['hello'.encode('utf8')] # 瀏覽器顯示的內容
if __name__ == '__main__':
# demo_app 是一個函數,用來處理用戶的請求
httpd = make_server('', 8000, demo_app)
sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
# 代碼的作用是打開電腦的瀏覽器,並在瀏覽器裡輸入 http://localhost:8000/xyz?abc
# import webbrowser
# webbrowser.open('http://localhost:8000/xyz?abc')
# 處理一次請求
# httpd.handle_request()
httpd.serve_forever() # 服務器在後臺一致運行
自定義 WSGI服務器
from wsgiref.simple_server import make_server
def demo_app(environ, start_response):
# environ是一個字典,保存了很多數據
# 其中最重要的是 PATH_INFO 能夠獲取到用戶的訪問路徑
path = environ['PATH_INFO']
print('path= {}'.format(path))
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ('sss', 'dddd')])
return ['你好'.encode('utf8')]
if __name__ == '__main__':
httpd = make_server('', 8080, demo_app)
sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()