歡迎光臨
每天分享高質量文章

如何理解 Python Web 開發?

作者:俞坤

原文:https://www.yukunweb.com/2018/2/understand-python-web/

因為 python代碼的優雅美觀且易於維護這一特點,越來越多的人選擇使用 Python做Web開發。而 Python的 Web框架百花齊放,目前比較流行的框架有大包大攬的 Django,小巧靈活的 Flask、 Bottle,還有性能高效的異步框架 Tornado、 sanic。這麼多框架只要選擇一個,閱讀他的文件,就可以很輕鬆的搭建一個 web app,完全不需要去管他實現的原理。

本篇文章意在對一個web開發做一個梳理。

前端網頁三劍客

我們打開瀏覽器輸入一個網址 yukunweb.com,然後就看到了瀏覽器給我們顯示的頁面,這個時候打開瀏覽器開發者工具,點擊 Network,掃清頁面,會看到下方的請求的 url,點擊 Response,就可以看到服務器傳回給瀏覽器的 html檔案信息了。如果複製 Response響應的內容,儲存為 index.html並且在瀏覽器打開,依然可以看到首頁的內容,但是似乎缺少了一些頁面的樣式和功能。

這是因為當瀏覽器接收到首頁的 HTML原始碼後,它會根據 HTML的規則去顯示頁面,然後再根據 HTML里的鏈接,自動發送HTTP請求給服務器,拿到相應的圖片,和 JavaScript、 CSS等資源,最終顯示出一個完整的頁面。所以我們會在 Network下麵能看到很多額外的以 .js, .css等後綴的請求了。

其實我們看到的頁面就是瀏覽器按照 HTML的規則,展示給我們的。 HTML告訴瀏覽器那裡是導航,那裡是主欄,那裡是側欄。而這些信息如何顯示,或者是顯示的樣式,就是 CSS檔案的功勞。至於比如導航的下拉隱藏上拉顯示就是 JavaScript的作用。

如果想要做Web開發,就一定得熟悉 HTML、 CSS、 JavaScript三劍客的知識,這裡推薦W3school的前端教程,也是我學習前端的地方:W3school

客戶端和服務器通信

理解了前段三劍客,就知道如何去寫一個網頁。那麼從我們在瀏覽器的地址欄輸入 URL,到 Web頁面呈現出來到底經歷了什麼。

如圖,一般這種通過發送請求獲取服務器資源的Web瀏覽器,都可以稱為客戶端(client)。首先發送一個請求(request)給服務器,大多是以GET請求方式訪問,服務器接收到你的請求,然後取到請求的資源,傳回給客戶端。

服務器和客戶端之間交流是怎麼進行的呢,服務器是怎麼理解客戶端的請求的呢。這裡就需要一種協議規範,就是HTTP(HyperText Transfer Protocol,超文本傳輸協議)。可以說, Web是建立在 HTTP協議上通信的。

如圖,仍然是之前的例子,打開瀏覽器訪問 yukunweb.com,打開瀏覽器開發者工具,點擊圖中標記的選項卡(記得點view parsed),可以看到客戶端發給服務器的請求頭前兩行。

  1. GET / HTTP/1.1

  2. Host: www.yukunweb.com

第一行開頭的GET表示請求訪問服務器的型別,稱為方法(method)。隨後的字符 /指明瞭請求訪問的資源物件,即請求URI。最後的 HTTP/1.1,即HTTP的版本號,用來提示客戶端使用的 HTTP協議功能。

綜上所述,第一行請求內容的意思是:請求訪問某台 HTTP服務器上的 /(首頁)頁面資源。所以第二行的 Host表示請求的域名也就是服務器所在地址。

如圖,如果是 POST請求的話,不僅會有請求頭部信息,還有一個 Form Data的請求物體內容。

接收到請求的服務器呢,他會將請求內容的處理結果以響應的形式傳回,看圖中的第一行:

開頭的部分仍然是服務器對應的 HTTP版本,緊接著的 200 OK表示請求的處理結果的狀態碼 (status code) 和原因短語。 200狀態碼就表示響應成功,常見的 404表示訪問錯誤, 500表示服務器響應錯誤。這裡的 OK是沒有固定的規則的,你也可以讓他傳回 GOOD啥的。

下一行是服務器信息,本站用的是 Nginx服務器,在下一行顯示了創建響應的日期時間。在下一行的 Content-Type表示內容的型別,客戶端會依賴他判斷響應的內容是網頁還是音頻,圖片等型別。

這裡只是簡單的介紹了 HTTP協議,即是客戶端與服務器之間的通信協議。如果想要深入瞭解推薦閱讀《HTTP權威指南》。

WSGI

如果你瀏覽一個地址 http://www.yukunweb.com/search-result/?keywords=音樂,你會訪問到本站的音樂關鍵詞的搜索結果。我們知道客戶端發送請求給服務器,那麼服務器是怎麼拿到資源的呢。其實這是交給後端運行的應用傳回的,好比你抓取一個頁面到獲取到信息,這些邏輯的處理肯定是我們的程式再跑。

但是,接收並且解析客戶端的 HTTP請求在發送 HTTP響應這些底層操作,後端的程式肯定是不會去處理的。所以,要想只專註於Web業務邏輯,還需要一個服務器和 web應用之間的嫁接層————WSGI。

什麼是WSGI(Web Server Gateway Interface)?

WSGI翻譯過來就是Web服務器網關接口。他只是一個規範,定義了 Web服務器如何與 Python應用程式進行交互,使得使用 Python寫的 Web應用程式可以和Web服務器(nginx/apache)對接起來。

該規範的地址:PEP 333

WSGI是 Python的Web開發的基石,有了它你就有了一切,它存在的目的有兩個:

  • 描述 Web 服務器如何與 Web 應用程式交互(將客戶端請求傳給應用程式),

  • 描述 Web 應用程式如何處理請求和如何傳回資料給服務器。

由於 Python內置的標準庫里有一個 WSGI庫 wsgiref,我們基於他來寫一個體現 WSGI目的的例子:

  1. from wsgiref.simple_server import make_server

  2. def application(environ, start_response):

  3.    status = '200 OK'

  4.    response_essay-headers = [('Content-type', 'text/html')]

  5.    start_response(status, response_essay-headers)

  6.    body = 'Hello, {name} !!!'.format(name=environ['PATH_INFO'][1:] or 'WSGI')

  7.    return [body.encode('utf-8')]

  8. app = make_server('', 8000, application)

  9. app.serve_forever()

運行程式,如果沒有報錯,此時打開瀏覽器輸入地址 127.0.0.1:8000和 127.0.0.1:8000/GuTianle,就可以看到程式傳回的頁面了。如圖:

我們可以看到一個請求,他的入口只需要一個 WSGI的處理函式。因為所有的請求信息都包含在 environ中,這樣我們就可以根據這些信息去傳回不同的資料。

引數:

  • environ:字典型別,存放了所有和客戶端相關的信息。如果想知道他裡面有哪些引數,可以更改上面的代碼在 return 行上面加一個 for k, v in environ.items()的迴圈,打印出字典里的所有引數。

  • startresponse:一個可呼叫物件,接收兩個必選引數和一個可選引數:

    • status: 一個字串,表示 HTTP 響應狀態字串,如 200,404

    • responseessay-headers: 一個串列,包含有如下形式的元組:(essay-headername, essay-headervalue),用來表示 HTTP 響應的 essay-headers ,如('Content-type', 'text/html')

    • exc_info(可選): 用於出錯時,服務器需要傳回給瀏覽器的信息

傳回:一個可迭代物件, 服務器通過遍歷這個可迭代物件可以獲得body的全部內容,內容可以是 html也可以是 json

這裡簡單的介紹了 WSGI是什麼,乾什麼。如果理解了 WSGI,那麼寫一個 Python的Web框架就很簡單了。這也是為什麼 Python有成百上千web框架的原因。

實現基於WSGI的框架

上面我們理解了 WSGI是乾什麼的,那麼我們基於它實現一個簡單的 web框架可以說輕而易舉了。

  1. from wsgiref.simple_server import make_server

  2. class Application(object):

  3.    def __init__(self, environ, start_response):

  4.        self.start_response = start_response

  5.        self.path = environ['PATH_INFO']

  6.    def __iter__(self):

  7.        if self.path == '/':

  8.            status = '200 OK'

  9.            response_essay-headers = [('Content-type', 'text/html')]

  10.            self.start_response(status, esponse_essay-headers)

  11.            yield 'Hello,World!'.encode('utf-8')

  12.        elif self.path == '/wsgi':

  13.            status = '200 OK'

  14.            response_essay-headers = [('Content-type', 'text/html')]

  15.            self.start_response(status, response_essay-headers)

  16.            yield 'Hello,WSGI!'.encode('utf-8')

  17.        else:

  18.            status = '404 NOT FOUND'

  19.            response_essay-headers = [('Content-type', 'text/html')]

  20.            self.start_response(status, response_essay-headers)

  21.            yield '404 NOT FOUND'.encode('utf-8')

  22. if __name__ == "__main__":

  23.    app = make_server('127.0.0.1', 8000, Application)

  24.    print('Serving HTTP on port 8000...')

  25.    app.serve_forever()

這個 Application類只不過是對 WSGI又做了一層簡單的封裝而已,由於上面說過 WSGI函式傳回的是一個可以迭代物件,所以需要實現一個iter方法,裡面控制了客戶端的請求路由並且傳回不同的輸出。

當然如果你想擴展成一個像樣的框架還需要考慮很多,比如像 flask那樣方便的路由系統,還有對於用戶請求方式的處理等等。總之是個很需要折騰的過程,好比 flask0.1版本去掉註釋也就 200 多行,而如今最新版本。。。


●編號428,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

Web開發

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演算法與資料結構、黑客技術與網絡安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

赞(0)

分享創造快樂

© 2022 知識星球   网站地图