WSGI到底是什麼?

在用Python Web開發時經常會遇到WSGI,所以WSGI到底是什麼呢?本文我們一起來揭開WSGI神祕的面紗!

先來看一下WSGI的介紹

全稱Python Web Server Gateway Interface,指定了web服務器和Python web應用或web框架之間的標準接口,以提高web應用在一系列web服務器間的移植性。 具體可查看 官方文檔

從以上介紹我們可以看出:

  1. WSGI是一套接口標準協議/規範;
  2. 通信(作用)區間是Web服務器和Python Web應用程序之間;
  3. 目的是制定標準,以保證不同Web服務器可以和不同的Python程序之間相互通信

你可能會問,爲什麼需要WSGI?

首先,我們明確一下web應用處理請求的具體流程:

  1. 用戶操作操作瀏覽器發送請求;
  2. 請求轉發至對應的web服務器
  3. web服務器將請求轉交給web應用程序,web應用程序處理請求
  4. web應用將請求結果返回給web服務器,由web服務器返回用戶響應結果
  5. 瀏覽器收到響應,向用戶展示

可以看到,請求時Web服務器需要和web應用程序進行通信,但是web服務器有很多種啊,Python web應用開發框架也對應多種啊,所以WSGI應運而生,定義了一套通信標準。試想一下,如果不統一標準的話,就會存在Web框架和Web服務器數據無法匹配的情況,那麼開發就會受到限制,這顯然不合理的。

既然定義了標準,那麼WSGI的標準或規範是?

web服務器在將請求轉交給web應用程序之前,需要先將http報文轉換爲WSGI規定的格式。

WSGI規定,Web程序必須有一個可調用對象,且該可調用對象接收兩個參數,返回一個可迭代對象:

  1. environ:字典,包含請求的所有信息
  2. 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工具庫,具體的實現我們之後再詳細學習。

以上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章