前兩篇文章,我們從空間和時間的角度都對HTTP有了一定的學習和理解,那麼基於上一篇的HTTP發展的時間順序,我會在後面的文章由淺入深,按照HTTP版本內容的更迭,一邊介紹相關欄位的使用方法,一邊講解其特性和目的,並和大家一起手寫測試程式碼,學以致用。
當然在真正進入時間線之前,我們還還需要一些前置內容,本篇呢,會先帶大家去手寫一下HTTP的一個小栗子及相關設定修改方式。然後我還會根據測試的HTTP請求帶大家先熟悉一下HTTP的基本內容。
這就是本篇的所有內容啦~本來還想把HTTP的特點和方法也寫進來,但是覺得篇幅可能會太長,所以還是另起一篇吧。
其實這個小栗子的程式碼十分簡單,我們主要使用Node來作為伺服器端。然後還需要修改一下下原生的Hosts檔案。廢話不多說,想必大家的大刀都飢渴難耐了吧,啊哈哈哈哈。
我們先來把測試程式碼寫好,簡單的一批,我是直接從Node官網的檔案的首頁直接複製下來的:
const http = require("http"); const hostname = "127.0.0.1"; const port = 8080; const server = http.createServer((req, res) => { res.end("Hello Zaking World!This is Node"); }); server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });
這段程式碼十分簡單哈,就是啟動個服務,然後伺服器返回一段字串就完事了。然後我們直接在命令列中執行這段程式碼的檔案:
node ./server.js
嗯,你可以看到這個檔案的檔名叫做server。啟動成功後,我們直接開啟瀏覽器在位址列輸入http://127.0.0.1:8080/,就可以直接看到瀏覽器中顯示出了結果:
很順利,很完美~到了這一步,其實你已經完成並體驗了HTTP/0.9的所有內容。Get請求,返回字串。但是現在還只是通過原生的ip來存取我們啟動的服務,我們要在本地模擬一下域名,直接通過域名來存取我們啟動的Node服務,實驗一下Get請求。
首先,我們找一下本機的hosts檔案。Windows的hosts檔案在這裡:C:\WINDOWS\system32\drivers\etc\hosts。你可以直接複製下來在windows裡搜尋,注意儲存的時候需要管理員許可權,而mac的地址則是在這裡:/etc/hosts。可以通過命令列:open /etc/hosts開啟後修改,也可以直接找到檔案後修改。你可以在hosts中加一行,修改成這樣:
127.0.0.1 www.zaking.com
然後,別忘了用管理員許可權儲存一下,我們直接開啟瀏覽器,輸入www.zaking.com:8080。當然,這個域名你可以隨便起,就是本地自己玩嘛。我們可以看到瀏覽器同樣獲取到了我們想要的內容:
為什麼我們加了hosts的設定之後,就可以在本地通過域名來存取我們啟動的服務了呢?其實簡單來說,就是域名解析的過程。當瀏覽器存取www.zaking.com的時候,發現這不是一個ip那就肯定是域名了,於是會嘗試去存取一系列的域名伺服器,把這個域名轉換成ip,但是由於整個域名解析的體系實在是很複雜,如果每次都去存取伺服器確定域名對應的ip,那實在是慢死了。於是為了解決慢的問題,就出現了快取,當瀏覽器嘗試去獲取對應域名的ip的時候,會先去瀏覽器自己的快取裡看看有沒有這個域名的IP,沒有就去作業系統裡找,再沒有,就會去檢查本機的域名解析檔案,也就是hosts,於是,找到了就直接轉換成這個對應的ip了。
那要是本機沒找到呢?那沒辦法,就得一級一級的去域名伺服器找對應的ip啦。
OK,我們的基本例子程式碼和最最最簡單的實驗都做完了。接下來的篇章我們都基於這個最簡單的例子,去試驗各個HTTP的核心重點。
還記得我們在空間穿梭的時候說過,當傳送請求的時候,會在HTTP報文的基礎上每一層都會加上上一層的頭資訊,然後當伺服器獲取,路過各層的時候,會拆下來各層需要的頭資訊。那HTTP其實也類似,也需要在傳輸的資料前附加一些頭資料。唯一不太一樣的是,HTTP的這些頭資料,是純文字,也就是ASCII碼,所以這些資料用肉眼就能分辨,不需要藉助解析工具。
HTTP協定是基於請求—響應的模型,所以頭資料也分為請求報文和響應報文。這兩種報文的結構也是基本相同的,都由三大部分組成:
看起來好像有點複雜,在通常情況下,我們會把起始行和頭欄位統一叫做請求頭或者響應頭,也就是header,而傳輸的資料就叫做body。這樣看起來是不是簡單了些。
我還記得我說要按照HTTP的歷史時間順序來講解HTTP,所以,現在應該講的是HTTP/0.9,但是HTTP/0.9又太少了,所以我只能把它包含在這裡了。
後面,我會按照總分的方式來講後續的內容,總的就是總體的介紹、比如HTTP的方法、狀態碼、特點等等,分的部分呢,則是按照時間順序來講解頭欄位。這樣,體系就清晰了。
不多囉嗦啦,我們繼續~
起始行這個東西其實有兩種叫法,傳送請求的起始行就叫做請求行,傳送響應的起始行就叫做狀態行。我們分別來看下他們的區別。
我們來看張圖:
這就是我們上面的基礎例子的HTTP請求的詳細headers。誒?你這糊弄我呢?你這也沒有啥請求行啊,不都是頭欄位麼?糊弄我不懂呢?嗯……你看我後面慢慢給你編,上面這個圖先看下就好,咱們每天都接觸,不看也行啦。
請求行由三部分構成,分別是:
你還記得HTTP的一個描述是:HTTP是使用純文字傳輸的,那麼在文字中,如何分割各個欄位呢?嗯……就是空格,用空格來分割請求方法、請求目標以及版本號,然後,再用一個換行符來作為請求行結束的標記。
我們還是回到之前的例子中:
看到跟之前的圖有什麼區別了嗎?這就是請求行原始的樣子,而我們上圖中看到的是parsed之後的,也就是經過了格式化,讓我們看起來更舒服。而source則可以讓我們看到報文的原始樣子。
而在請求行中,實際上我們要重點關注的就是請求方法,這個我們下一章再說,我們先知道請求行都有哪些內容就好啦。
響應報文中的起始行不叫響應行,而是叫狀態行,狀態行也有三部分:
狀態行就像這樣:
那你知道為啥響應報文中的起始行不叫響應行了不?重點就在這個狀態碼,告訴你對於當前請求的處理結果的狀態。而狀態碼和原因則往往是配對出現的。
那麼請求中有「方法」,響應中有「狀態」。剛好,來回都描述的很完整。
請求行或者狀態行再加上頭欄位,就是完整的請求頭或者響應頭。請求頭和響應頭的頭欄位基本上是一致的,都是一種key-value形式的鍵值對。但是頭欄位有以下幾點需要注意一下:
HTTP協定規定了好多好多的頭欄位,甚至我們還可以自定義頭欄位,但是通常頭欄位可以分為以下幾類:
所以你看,原則上欄位的型別就三種,要麼只有我能用,要麼只有你能用,要麼就都能用。
當然,還有一種情況是往往有些欄位不是獨立出現的,而是有一定的配合~我們後面會詳細聊噠。
實體,其實就是我們在使用HTTP的時候傳輸的資料內容,我們通常把它叫做body,當然,我們也可以通過GET方法來傳遞一定資料量的資訊,0.9就是這樣做的,但是畢竟這算不上是「正途」,如果我想要傳音訊、視訊、圖片咋整?總不能也用GET請求放在URL的query裡吧?
這就需要我們的body了,但是body要怎麼傳呢?我傳給你伺服器一個視訊檔,但是伺服器怎麼知道要按照什麼樣的方式來解析這個檔案呢?嗯……可以根據檔案的字尾吖,沒錯,那我要是改了字尾名呢?我就故意改,你能咋整,所以這種方法也不那麼靠譜。
然後,其實某一個型別的檔案的二進位制編碼的前面一小部分其實是固定的,我們也可以根據這些固定的資料來判斷,但是這樣也好像不是那麼靠譜,而且還有點低效,還有很大概率查不出來。
那咋整,那就是MIME type,MIME是一個很大的標準規範,HTTP拿取了其中的一部分用來表示body的資料型別。那啥是MIME呢?嗯,它的大名叫做「多用途網際網路郵件擴充套件」(Multipurpose Internet Mail Extensions),本來是給電子郵件用的,目的是為了讓電子郵件可以傳送出了ASCII碼以外的任意資料。
MIME把資料分為了八大類,每個大類下面還有子類,大概是這樣的:type/subtype。就很符合HTTP的標準要求,於是就順手牽羊拿過來了。
在HTTP中常用的大概有text、image、audio/video、application等。最常見的大家也最熟悉的想必就是application/json啦。
至於具體每一個欄位的詳細解釋,我會在後面講解到對應欄位的時候再加以詳述。
我們稍稍回顧一下本篇我們都學習了哪些內容。
首先,我帶大家完成了一個小小小栗子,看了一下GET請求。
然後,依賴於這個小小小栗子,我們知道了HTTP的組成有哪三部分或者哪兩部分。
最後我問大家一個問題:你知道起始行中的重點內容有哪些麼?
我們下一篇再見~嘿嘿