大家好,我是軍哥 我們學(xué)知識大致都會經(jīng)歷三個階段。第一階段是what,了解一個東西是什么,第二階段是how,了解一個東西如何使用。第三階段是why,為什么要這么用。當(dāng)你達(dá)到第三層境界才能說明你對這個東西理解透徹。 同理,對于一個做web開發(fā)的程序員來說,沒有什么比自己寫一個web服務(wù)器或web框架更能了解web編程的本質(zhì)了。 這個系列是我的一位讀者朋友江湖十年寫的,他過來問我可不可以投稿在公眾號。看完之后就立馬答應(yīng)了。大家都喜歡看沒營養(yǎng)的爽文,雖然這種干貨型的技術(shù)文看的人少,但是能幫助對web開發(fā)感興趣的人就值了。 系列文章工作分為8章,除了這篇,次條到第8條都是,你也不可能一天看到,先收藏著,看看你能不能一周也寫一個web服務(wù)器出來。 教程簡介本教程使用 Python 語言實(shí)現(xiàn)了一個簡易版的 Web 服務(wù)器,從 Web 開發(fā)基礎(chǔ)開始講解,不使用任何第三方庫或框架,通過實(shí)現(xiàn)一個 Todo List 應(yīng)用來還原 Web 開發(fā)的本質(zhì)。 教程所需基礎(chǔ)Python、HTML、CSS 語法基礎(chǔ),對 Web 開發(fā)基本概念有所了解。 教程面向讀者本教程為入門級,主要適合準(zhǔn) Python Web 開發(fā)者或?qū)?Web 開發(fā)理解不是很透徹的同學(xué)。 教程特點(diǎn)本教程并不會使用如 Django、Flask 等常見 Python Web 開發(fā)框架,也不會使用任何其他第三方庫,甚至?xí)梦募硖娲鷶?shù)據(jù)庫存儲數(shù)據(jù)。其目的是為了簡化一些對初學(xué)者來說看似復(fù)雜的概念,使用更少的依賴從零開發(fā)一個 Python Web 服務(wù)器。以便讀者能夠更深刻的理解 Web 開發(fā)。 前言Web 開發(fā)技術(shù)一直在高速發(fā)展,各種新奇概念與框架層出不窮,尤其在 Web 前端領(lǐng)域,幾年前還是 jQuery 的天下,而如今在 Vue、React 等框架面前也顯得廉頗老矣。 不過,雖然各種框架技術(shù)日新月異,但 Web 開發(fā)的核心概念與本質(zhì)依舊不曾改變,本教程將通過一個 Todo List 應(yīng)用帶你探索 Web 開發(fā)基本原理,只有真正明白了 Web 開發(fā)的核心基礎(chǔ),才能更輕松的應(yīng)對新框架與技術(shù)。 Web 開發(fā)簡介我們常見的軟件種類有桌面軟件、移動 APP以及網(wǎng)頁應(yīng)用等,Web 開發(fā)通常就是在開發(fā)網(wǎng)頁應(yīng)用。桌面軟件、移動 APP 需要先安裝在 Windows、Android 等宿主機(jī)才能使用,每個客戶端每次升級更新軟件時都需要重新下載并安裝。而 Web 應(yīng)用所依賴的客戶端是瀏覽器,實(shí)際數(shù)據(jù)都存儲在遠(yuǎn)程服務(wù)器端,如果應(yīng)用需要升級,那么只需要升級服務(wù)器,所有用戶通過瀏覽器打開網(wǎng)頁時都將實(shí)時獲取最新的數(shù)據(jù),這也是 Web 開發(fā)能夠流行起來的很重要的原因。 HTTP 簡介要學(xué)習(xí) Web 開發(fā),首先要明白什么是 HTTP 協(xié)議,因?yàn)?Web 開發(fā)就是建立在 HTTP 協(xié)議之上的。 在瀏覽器地址欄輸入網(wǎng)址 https://www.jd.com/ 將得到京東商城首頁。我們在瀏覽器頁面中看到的所有數(shù)據(jù)都是服務(wù)器通過 HTTP 協(xié)議傳輸過來的。
HTTP 協(xié)議中文叫超文本傳輸協(xié)議,可以拆分成三部分理解:超文本、傳輸、協(xié)議。 所謂超文本就是 HTML、CSS、圖片、視頻等內(nèi)容的集合。傳輸既超文本內(nèi)容從瀏覽器(客戶端)到服務(wù)器或從服務(wù)器到瀏覽器之間的傳輸過程。而協(xié)議是規(guī)范,大家約定俗成的規(guī)約既是協(xié)議。 HTTP 基于請求 —— 響應(yīng)模型,瀏覽器向服務(wù)器的某個網(wǎng)址發(fā)起請求,服務(wù)器響應(yīng)瀏覽器對應(yīng)的資源。以下就是一個 HTTP 請求 —— 響應(yīng)的模型圖。 左側(cè)是瀏覽器,右側(cè)是服務(wù)器。瀏覽器和服務(wù)器之間通訊是通過文本傳輸來完成的。瀏覽器發(fā)起請求時發(fā)送的數(shù)據(jù)叫作請求報(bào)文,得到的服務(wù)端響應(yīng)的數(shù)據(jù)叫作響應(yīng)報(bào)文。下圖示展示了請求報(bào)文和響應(yīng)報(bào)文的格式。 根據(jù)圖示可以看到,無論是請求報(bào)文還是響應(yīng)報(bào)文基本都分為四個部分,每個部分之間以 \r\n 作為換行分隔符。 請求報(bào)文包含請求行、報(bào)文首部、空行、報(bào)文主體四個部分。 響應(yīng)報(bào)文包含狀態(tài)行、報(bào)文首部、空行、報(bào)文主體四個部分。 在請求報(bào)文中,請求行和報(bào)文首部可以看成一個整體叫作請求頭,報(bào)文主體又叫請求體。 在響應(yīng)報(bào)文中,狀態(tài)行和報(bào)文首部可以看成一個整體叫作響應(yīng)頭,報(bào)文主體又叫響應(yīng)體。 如何在 Chrome 瀏覽器中查看請求與響應(yīng)的報(bào)文信息呢?瀏覽器頁面任意位置點(diǎn)擊鼠標(biāo)右鍵 —— 選擇檢查,就能夠在頁面底部顯示 Chrome 開發(fā)者工具。點(diǎn)擊 Elements 選項(xiàng)卡,你就能夠看到網(wǎng)頁的源代碼,也就是服務(wù)器的響應(yīng)數(shù)據(jù)。 點(diǎn)擊 Network 選項(xiàng)卡,就能夠看到所有請求、響應(yīng)記錄。 每一行記錄即為一個請求,為什么只是在地址欄里輸入了 https://www.jd.com/ 會出現(xiàn)這么多請求呢?我們知道,HTML 中不止可以寫簡單的文本標(biāo)簽,還可以寫 img 、video 等多媒體標(biāo)簽加載圖片或視頻,以及 link 、script 等標(biāo)簽來加載 CSS 樣式 和 JavaScript 腳本。瀏覽器在得到服務(wù)器的第一個響應(yīng)時,會檢查服務(wù)器返回的 HTML 頁面,如果頁面源碼中包含這些特殊的標(biāo)簽,瀏覽器就會針對每一個標(biāo)簽專門發(fā)起一次請求。
點(diǎn)擊第一個請求,在右側(cè)將會顯示該請求及響應(yīng)信息。找到 Request Headers 點(diǎn)擊右側(cè)的 view source ,將會看到此次請求的請求頭信息。 第一行 GET / HTTP/1.1 即為請求行。它由三個部分組成:請求方法、請求地址、HTTP 協(xié)議版本號,分別對應(yīng) GET 、/ 、HTTP/1.1 。每個部分之間通過一個空格隔開。 HTTP 請求方法有很多,不過最常見的只有四種:POST 、DELETE 、PUT 、GET 分別對應(yīng)增、刪、改、查四個操作。由于我們只是想查看京東首頁,所以這里發(fā)送的是 GET 請求。 請求地址 / 代表首頁,你可能看過如 /index 、/index.html 等類似的地址,其實(shí)這只是一個約定俗成的做法,它們都代表首頁。 現(xiàn)在最常用的 HTTP 版本即為 1.1 。雖然 HTTP 2.0 早已發(fā)布,不過目前來說普及率依舊不是很高。所以現(xiàn)在大可不必糾結(jié)版本的問題。 從第二行開始,所有內(nèi)容都是諸如 Key: Value 這種鍵值對的形式組合成的,我們管每一組鍵值對都叫作請求首部字段,這些首部字段加在一起就是請求的報(bào)文首部,一般會直接叫請求頭。 其中最重要的請求首部字段就是 Host: www.jd.com 這一行,因?yàn)橹挥羞@一行是 HTTP 協(xié)議明確規(guī)定為必傳的首部字段,其他請求首部字段都是非必傳字段。Host: www.jd.com 的作用是當(dāng)服務(wù)器上部署了多個網(wǎng)站,那么服務(wù)器就會根據(jù)這一行的信息來確定瀏覽器到底想訪問哪個網(wǎng)站。 Connection: keep-alive 代表長連接,User-Agent 標(biāo)識了瀏覽器信息,Accept 代表瀏覽器能夠接收的報(bào)文格式,其他的請求首部字段可以等我們用到了再做說明。
在 HTTP 請求報(bào)文中,還缺少一個空行和請求體是我們目前沒有看到的。實(shí)際上雖然我們是點(diǎn)擊瀏覽器的 view source 功能來查看請求報(bào)文信息,但它仍然是瀏覽器處理過的數(shù)據(jù),并不是原始數(shù)據(jù),所以空行是看不到的。而此次請求為 GET 請求,通常 GET 請求是沒有請求體的,所以請求體被省略了。 一個 HTTP GET 請求大概長這樣: GET / HTTP/1.1 Host: www.jd.com Connection: keep-alive User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
一個 HTTP POST 請求大概長這樣: POST /login HTTP/1.1 Host: www.jd.com Connection: keep-alive User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
username=test&password=pass
上面 POST 請求示例中 username=test&password=pass 即為請求體。 需要注意,以上請求報(bào)文中每一個換行都是用 \r\n 來標(biāo)識的,這一點(diǎn)在后面實(shí)際開發(fā)的章節(jié)中就能體會到。 分析完請求報(bào)文,接下來分析下響應(yīng)報(bào)文。找到 Response Headers 同樣點(diǎn)擊右側(cè)的 view source ,將會看到此次請求的響應(yīng)頭信息。 第一行 HTTP/1.1 200 OK 即為狀態(tài)行。它同樣由三部分組成:HTTP協(xié)議版本號、狀態(tài)碼、當(dāng)前狀態(tài)碼對應(yīng)的原因短語,分別對應(yīng) HTTP/1.1 、200 、OK 。每個部分之間仍然通過一個空格隔開。 其中狀態(tài)碼用來告知服務(wù)器返回的響應(yīng)狀態(tài),為一個數(shù)字。狀態(tài)碼大概分為四類:2XX 、3XX 、4XX 、5XX ,分別對應(yīng)成功、重定向、客戶端錯誤、服務(wù)端錯誤。所以狀態(tài)碼為 200 表示請求成功。 原因短語 OK 其實(shí)并不是很重要,它主要是給客戶端返回一個人類可讀的短語,來標(biāo)識請求結(jié)果,對瀏覽器來說沒什么作用。 響應(yīng)首部字段同樣是 Key: Value 這種鍵值對形式,并且有些是和請求首部字段一樣的,稱為通用首部字段,如 Connection: keep-alive 同樣存在于響應(yīng)首部字段。還有些字段是和請求首部字段搭配著使用的,如你所見 Content-Type 字段就是搭配請求首部字段中的 Accept 來使用的,Accept 是瀏覽器用來告訴服務(wù)器它能夠接收的數(shù)據(jù)格式,而 Content-Type: text/html 標(biāo)識了服務(wù)器返回的數(shù)據(jù)格式為 html 。其他的響應(yīng)首部字段可以等我們用到了再做說明。 同樣的,響應(yīng)報(bào)文中的空行并不會在瀏覽器中展現(xiàn)出來,不過響應(yīng)體是可以在瀏覽器中看到的。 當(dāng)前請求的響應(yīng)體實(shí)際上就是服務(wù)器返回的 HTML 源碼。 一個 HTTP 響應(yīng)大概長這樣: HTTP/1.1 200 OK Connection: keep-alive Content-Type: text/html
<html>XXX</html>
URL 簡介介紹完 HTTP 協(xié)議基礎(chǔ),我們再來簡單介紹下 URL 。 京東首頁的網(wǎng)址是 https://www.jd.com/ ,這就是一個 URL 。URL 中文叫作統(tǒng)一資源定位符。URL 的作用是在網(wǎng)絡(luò)中唯一的標(biāo)記一個資源。 你可能聽說過 URI ,實(shí)際上 URL 是 URI 的一個子集。在基礎(chǔ)學(xué)習(xí)階段,我們并不需要弄清楚它們之間到底有什么差別,只需要把我們常見的網(wǎng)址統(tǒng)稱為 URL 即可。 一個 URL 的完整格式如下: scheme://user:passwd@host:port/path?query#fragment
每一個部分代表含義如下: scheme :協(xié)議名,表示資源使用哪種協(xié)議,http 就代表了 HTTP 協(xié)議,file 代表文件協(xié)議。
:// :這個是固定寫法,記住就好,沒必要深究。
user:passwd@ :身份信息,表示訪問主機(jī)時需要提供的用戶名和密碼,但這種將隱私信息完全暴露在外部的作法已經(jīng)幾乎沒人使用了。
host:port :表示資源所在的主機(jī)名和端口號。主機(jī)名為 IP 地址或域名,必須要有,HTTP 協(xié)議默認(rèn)端口號是 80 ,可以省略,其他端口號則不可省略。
path :標(biāo)識資源所在位置。它采用類似 Unix 系統(tǒng)的目錄風(fēng)格,必須以 / 開頭,如果是多級路徑,可以寫成 /a/b/c 這種形式。
query :查詢參數(shù),在路徑之后第一個 ? 開始(不包含? )到 # 之前結(jié)束,所有的內(nèi)容都是查詢參數(shù),一個參數(shù)的格式為 key=value ,如果存在多個參數(shù),中間用 & 隔開。假如我們需要對數(shù)據(jù)做分頁處理就可以使用這個參數(shù),如 page=1&offset=20 表示需要獲取第一頁的數(shù)據(jù),每頁數(shù)據(jù) 20 條。這時候服務(wù)器端就可以解析到這個參數(shù)并返回對應(yīng)的數(shù)據(jù)。
fragment :最后一部分為片段標(biāo)識符。也可以將其叫做錨點(diǎn),從 # 開始,瀏覽器可以根據(jù)錨點(diǎn)跳轉(zhuǎn)到頁面指定位置,但這個參數(shù)是不會被發(fā)送到服務(wù)器端的,所以開發(fā)服務(wù)器端通常不太需要關(guān)心。
下面通過對一個實(shí)際的 URL 地址進(jìn)行分析來加強(qiáng)理解。 以下圖示為在京東首頁搜索 Python書籍 后瀏覽器地址欄 URL 截圖。為了能夠更清晰的說明問題 URL 部分略有刪減。 我們可以將 URL 復(fù)制出來: https://search.jd.com/Search?keyword=python%E4%B9%A6%E7%B1%8D&enc=utf-8
以上 URL 按標(biāo)準(zhǔn)格式拆分如下: scheme: https host:port: search.jd.com path: /Search query: keyword=python%E4%B9%A6%E7%B1%8D&enc=utf-8
你應(yīng)該已經(jīng)發(fā)現(xiàn)上面復(fù)制出來的 URL 和截圖中的有些不同。 首先我們復(fù)制出來的 URL 是以 https 開頭,而瀏覽器中卻是以 search 開頭,其實(shí)這是瀏覽器為了讓 URL 更加可讀,而將 https:// 隱藏了,實(shí)際上它仍然是存在的。 你可能有個疑惑,我們上面一直在介紹 HTTP 協(xié)議,一個完整的 URL 是以 http 開頭的。但實(shí)際上京東網(wǎng)站的 URL 都是以 https 開頭的。其實(shí) https 是基于 http 的,只是在 http 的基礎(chǔ)上又加了一個安全套接層,默認(rèn)端口 443 ,HTTP 協(xié)議傳輸數(shù)據(jù)都是明文傳輸,HTTPS 協(xié)議傳輸數(shù)據(jù)是經(jīng)過加密的,所以使得數(shù)據(jù)的傳輸更加安全,僅此而已。本教程不會使用 HTTPS 協(xié)議,故此不做過多講解。 還有一個較大的差異就是在瀏覽器地址欄中看到的查詢參數(shù) Python書籍 復(fù)制出來以后就變成了 python%E4%B9%A6%E7%B1%8D 。這是 URL 編碼造成的,通常在 URL 中只能使用 ASCII 碼,并且 URL 本身會使用如 ? 、& 等符號作為分隔符。這就導(dǎo)致了很多特殊符號或者 ASCII 以外的字符(如中文)不能夠直接使用,所以就規(guī)定了一種轉(zhuǎn)碼規(guī)范,將不能直接用于 URL 的字符通過轉(zhuǎn)義操作使其變?yōu)榭捎米址_@就是 書籍 兩個字符變成了 %E4%B9%A6%E7%B1%8D 的原因。在瀏覽器地址欄中能夠正常顯示同樣是瀏覽器為了讓 URL 更加可讀做的特殊處理。URL 轉(zhuǎn)義具體規(guī)則可以查看相關(guān)文檔進(jìn)行學(xué)習(xí)。 TCP/IP 簡介多個計(jì)算機(jī)之間通訊靠的是網(wǎng)絡(luò)協(xié)議,最早的計(jì)算機(jī)廠商生產(chǎn)的計(jì)算機(jī)只能跟自家的計(jì)算機(jī)通訊,為讓所有計(jì)算機(jī)之間都能夠通訊就有了 TCP/IP 協(xié)議。這就比如幾個人對話,有的說漢語、有的說英語、有的說日語,大家誰也聽不懂其他人說話,所以最后大家都規(guī)定用一種語言來交流一樣。 TCP/IP 協(xié)議并不是單個的協(xié)議,它是一個協(xié)議族,包含了很多種網(wǎng)絡(luò)通訊協(xié)議,HTTP 協(xié)議也在其中。在網(wǎng)絡(luò)中兩臺計(jì)算機(jī)之間如果需要通訊,就要知道對方在哪,所以就有了 IP 地址。就像我們要給別人發(fā)快遞,那么就要知道對方的家庭住址一樣?,F(xiàn)在常用的 IP 地址為 IPv4 版本,格式如 192.168.3.14 ,最新版本是 IPv6,格式如 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 。 有了 IP 地址還不夠,就像你把快遞郵寄到一個家庭住址,但家里有五口人,你還要確定這個快遞是寄給誰的。端口的作用就是干這個的。計(jì)算機(jī)通過端口號區(qū)分收到的信息該轉(zhuǎn)發(fā)給哪個軟件。 IP 地址加上端口號就是上面介紹的 URL 中 host:port 部分,如 192.168.3.14:80 。但 IP 地址是一串?dāng)?shù)字,不容易記住,所以就有了域名。www.jd.com 就是京東的域名。我們在瀏覽器中輸入這個域名時,計(jì)算機(jī)會自動將其轉(zhuǎn)換成 IP 地址加端口號的形式。這個轉(zhuǎn)換過程是通過一個叫 DNS 解析的技術(shù)。它能夠?qū)⒂蛎?IP 之間的關(guān)系做綁定,知道了域名它就能查出其對應(yīng)的 IP 地址是多少。 HTTP 協(xié)議是建立在 TCP/IP 協(xié)議之上的,HTTP 的數(shù)據(jù)傳輸依靠的是 TCP 協(xié)議??偨Y(jié)起來就是 TCP 用來傳輸數(shù)據(jù),HTTP 用來規(guī)范數(shù)據(jù)傳輸格式。 擴(kuò)展從瀏覽器地址欄輸入網(wǎng)址 https://www.jd.com/ 到看見京東網(wǎng)站首頁經(jīng)歷了什么? 瀏覽器從地址欄獲取 URL 瀏覽器或操作系統(tǒng)通過 DNS 解析將 URL 中域名和端口號解析成對應(yīng)的 IP 地址和端口號 瀏覽器通過 TCP 與服務(wù)器建立連接 瀏覽器向服務(wù)器發(fā)送請求報(bào)文 服務(wù)器收到請求報(bào)文后做對應(yīng)的處理,將處理結(jié)果組裝成響應(yīng)報(bào)文返回給瀏覽器 瀏覽器解析響應(yīng)報(bào)文,渲染頁面
以上就是從瀏覽器地址欄輸入網(wǎng)址 https://www.jd.com/ 到看見京東網(wǎng)站首頁所經(jīng)歷的過程概述。 如果你對 Web 開發(fā)完全零基礎(chǔ),看了以上我對 HTTP 協(xié)議的簡介,感覺不是很明白的話,那么我推薦你可以看一本叫作《圖解HTTP》的書,看完后再來學(xué)習(xí)本教程能夠達(dá)到事半功倍的效果。 開發(fā)環(huán)境本教程項(xiàng)目開發(fā)環(huán)境如下: Python:Python3.7 瀏覽器:Chrome83 開發(fā)工具:PyCharm 2020.1 操作系統(tǒng):macOS 10.15
有 Python 基礎(chǔ)的同學(xué)想必搭建開發(fā)環(huán)境肯定不在話下,所以具體搭建過程這里不再做過多介紹,需要強(qiáng)調(diào)的一點(diǎn)是瀏覽器我只推薦 Chrome 或者 Firefox,這樣能夠得到更好的開發(fā)體驗(yàn)。 聯(lián)系作者:
|