1. 程式人生 > >根據WSGI協議自己實現mini-web框架(1)

根據WSGI協議自己實現mini-web框架(1)

1. 為什麼要研究WSGI

我之前在學習web開發時,往往都是根據MVT等架構直接寫所謂的“檢視”,使用其本身自帶的http伺服器,當時就一直很好奇,為什麼有了HTTP協議之後,還要研究WSGI協議,它到底有什麼用?

由於“工作要緊”所以很多細枝末節的事情 都被安排到推遲,最終遲遲沒有研究

後來有個空閒時間的機會,終於可以靜下來研究WSGI

 

2. 說說WSGI與HTTP伺服器之間的呼叫過程

2.1 瀏覽器請求動態頁面過程

 

看完上圖估計也就知道了所謂的WSGI到底有什麼用,如果還是不明白,那我就通俗點說:

web伺服器接收到瀏覽器傳送過來的請求之後,需要將瀏覽器傳遞過來的很多資料(例如user-agent、cookie等)都要告訴你的“檢視”,因為只有“檢視”接收到資料,才能夠進一步處理,例如判斷是否登入等

所以這就需要web伺服器與“檢視”之間的傳遞,此時WSGI就是起到傳遞的作用,只不過這個傳遞的過程有些複雜 可以參考上圖2、3、4、6步驟

 

2.2 用個例子來說明WSGI

怎麼在你剛建立的Web伺服器上執行一個Django應用Flask應用,如何不做任何改變而適應不同的web架構呢?

在以前,選擇 Python web 架構會受制於可用的web伺服器,反之亦然。如果架構和伺服器可以協同工作,那就好了:

但有可能面對(或者曾有過)下面的問題,當要把一個伺服器和一個架構結合起來時,卻發現他們不是被設計成協同工作的:

那麼,怎麼可以不修改伺服器和架構程式碼而確保可以在多個架構下執行web伺服器呢?答案就是 Python Web Server Gateway Interface (或簡稱 WSGI,讀作“wizgy”)。

WSGI允許開發者將選擇web框架和web伺服器分開。可以混合匹配web伺服器和web框架,選擇一個適合的配對。

比如,可以在Gunicorn 或者 Nginx/uWSGI 或者 Waitress上執行 Django, Flask, 或 Pyramid。

真正的混合匹配,得益於WSGI同時支援伺服器和架構:

web伺服器必須具備WSGI介面,所有的現代Python Web框架都已具備WSGI介面,它讓你不對程式碼作修改就能使伺服器和特點的web框架協同工作。

WSGI由web伺服器支援,而web框架允許你選擇適合自己的配對,但它同樣對於伺服器和框架開發者提供便利使他們可以專注於自己偏愛的領域和專長而不至於相互牽制。

其他語言也有類似介面:java有Servlet API,Ruby 有 Rack。

2.3 定義WSGI介面

WSGI介面定義非常簡單,它只要求Web開發者實現一個函式,就可以響應HTTP請求。我們來看一個最簡單的Web版本的“Hello World!”:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return 'Hello World!'

上面的application()函式就是符合WSGI標準的一個HTTP處理函式,它接收兩個引數:
  • environ:一個包含所有HTTP請求資訊的dict物件;
  • start_response:一個傳送HTTP響應的函式。

整個application()函式本身沒有涉及到任何解析HTTP的部分,也就是說,把底層web伺服器解析部分和應用程式邏輯部分進行了分離,這樣開發者就可以專心做一個領域了

不過,等等,這個application()函式怎麼呼叫?如果我們自己呼叫,兩個引數environ和start_response我們沒法提供,返回的str也沒法發給瀏覽器。

所以application()函式必須由WSGI伺服器來呼叫。有很多符合WSGI規範的伺服器。而我們此時的web伺服器專案的目的就是做一個既能解析靜態網頁還可以解析動態網頁的伺服器

2.4 web伺服器-----WSGI協議---->web框架 傳遞的字典

下面的資訊,僅僅http傳遞給 web框架的部分資訊,沒有必要全部寫清楚,只要知道這裡有很多重要的資訊即可,而這些資訊在web框架中需要

{
    'HTTP_ACCEPT_LANGUAGE': 'zh-cn',
    'wsgi.file_wrapper': <built-infunctionuwsgi_sendfile>,
    'HTTP_UPGRADE_INSECURE_REQUESTS': '1',
    'uwsgi.version': b'2.0.15',
    'REMOTE_ADDR': '172.16.7.1',
    'wsgi.errors': <_io.TextIOWrappername=2mode='w'encoding='UTF-8'>,
    'wsgi.version': (1,0),
    'REMOTE_PORT': '40432',
    'REQUEST_URI': '/',
    'SERVER_PORT': '8000',
    'wsgi.multithread': False,
    'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'HTTP_HOST': '172.16.7.152: 8000',
    'wsgi.run_once': False,
    'wsgi.input': <uwsgi._Inputobjectat0x7f7faecdc9c0>,
    'SERVER_PROTOCOL': 'HTTP/1.1',
    'REQUEST_METHOD': 'GET',
    'HTTP_ACCEPT_ENCODING': 'gzip,deflate',
    'HTTP_CONNECTION': 'keep-alive',
    'uwsgi.node': b'ubuntu',
    'HTTP_DNT': '1',
    'UWSGI_ROUTER': 'http',
    'SCRIPT_NAME': '',
    'wsgi.multiprocess': False,
    'QUERY_STRING': '',
    'PATH_INFO': '/index.html',
    'wsgi.url_scheme': 'http',
    'HTTP_USER_AGENT': 'Mozilla/5.0(Macintosh;IntelMacOSX10_12_5)AppleWebKit/603.2.4(KHTML,likeGecko)Version/10.1.1Safari/603.2.4',
    'SERVER_NAME': 'ubuntu'
}

 

未完待續。。。

 

我的部落格:https://www.cnblogs.com/dong4716138/

我的網站:http://howdoit.cn/