如果說 HTTP 是網際網路的信使, 那麼 HTTP 報文就是他用來搬東西的包裹了. --- 《HTTP 權威指南》
HTTP 程式之間是通過互相傳送 HTTP報文(以下簡稱報文) 來工作的,那麼瞭解報文相關的知識就非常有必要了。知道報文的結構、內部的欄位以及各欄位的功能等資訊,這對以後的工作具有重要的意義。
1. HTTP報文 的概念和報文流的概念
1.1 報文的概念
報文是 HTTP 程式之間傳遞的資料塊,這個資料塊內包含了傳送者的目的、伺服器返回的結果以及其他相關資訊。
報文可以分為顧名就能思義的兩種:請求報文和響應報文。
請求報文是客戶端發出的,向伺服器請求資源的報文;響應報文就是服務端發出的,向客戶端進行回覆的報文。~(無論你的請求能不能實現,我總要回復你一下啊)~
1.2 關於報文流的兩個概念
程式之間進行通訊時會傳遞許多的報文,可以想象現實中的河流,水在小河中流動,而報文在各個程式間流動,稱為報文流。關於報文流有兩個概念:
-
流入(inbound)和流出(outbound) 這是個絕對的概念,這裡的絕對是對於伺服器來說的絕對:流向伺服器的報文流就是流入;從伺服器發出(流出)的報文流就是流出。流入和流出只是對於伺服器來說的,所以是絕對的。
-
上下游的概念 (upstream & downstream) 這是個相對的概念。
對於報文的傳送者來說,它就是這個報文的上游,後面接收到這個報文的節點就是他的下游;
對於報文的接收者來說,它就是報文的下游,在它之前收到這個報文的節點就是他的上游;如果它繼續將這個報文傳遞下去,那麼接下來收到這個報文的就是它的下游。
如圖:
上圖中的請求報文流從客戶端流向伺服器,中間經過了幾個代理,按流動的方向來看,代理1位於代理2的上游,而代理2位於代理3的上游。
而在響應流中,響應報文從伺服器流向客戶端,中間經過了幾個同樣的代理。此時按流動的方向來看,代理3位於代理2的上游,而代理2位於代理1的上游。
由此可見,由於報文只能向下流動,所以請求報文流中的上游就會成為響應報文流中的下游。這就體現了報文流中上下游的相對性。~風水輪流轉?~
2. HTTP 報文的結構
報文由3大部分組成,分別是:
- 起始行(start line):用來表明報文的目的或者執行的結果
- 首部(header):提供報文的附加資訊
- 實體的主體(body):要傳遞給對方的內容,比如文字、圖片、檔案
這樣看起來有些難以理解,可以用在淘寶買東西的過程打個比方:先決定自己要買什麼,收貨地址是什麼,然後下單,這就相當於起始行裡包含的資訊;賣家寄來的包裹裡有你要買的東西,還有使用說明書,那麼這個使用說明書就可以理解為首部(header); 你收到的東西就相當於主體(body)了。
雖然這個例子不是很嚴謹,但是也可以用來形成對這三個部分的感性理解了。
下面來依次討論一下這三個部分。
2.1 起始行 (start line)
首先要明確的一點是,請求報文和響應報文由於目的不同,起始行中所包含的資訊是有差別的。下面的內容會用 [C] (Client)來表示請求報文中包含的資訊, 用 [S] (Server)來表示響應報文中包含的資訊。
2.1.1 起始行中包含的資訊, 這些資訊在報文中會以空格來分隔
-
[C] 方法(method): 客戶端希望伺服器對資源做出的動作,例如我們熟悉的 GET 和 POST 方法。更詳細的內容將在文章後部分討論.
-
[C] 要請求的資源的URL(request URL): 要請求的資源在伺服器上的路徑
-
[C & S] 版本(version): 客戶端和伺服器所使用的HTTP協議的 版本號. 這樣就可以提示對方自己支援的 HTTP 方法了,防止版本不同造成的不相容問題。 例如: HTTP/1.1 表示這個報文的傳送者遵循的是1.1版本的 HTTP 協議.
-
[S] 狀態碼(status code): 描述伺服器根據客戶端的請求進行操作的結果的程式碼。例如著名的 404. 網上專門講狀態碼的文章已經很多了,在此不贅述. 更多更詳細的狀態碼描述見 MDN的相應內容.HTTP 響應程式碼
-
[S] 狀態短語(reason-phrase): 和狀態碼對應,是狀態碼所表示的資訊的另一種表示方法,為了人類能理解,而不是看到狀態碼的時候一下子反應不過來服這幾個數字代表的資訊到底是什麼。例如和 狀態碼404對應的 Not Found.
下面是兩個具體的例子:
-
請求報文的起始行
GET /test/hello.txt HTTP/1.1
可以看到這個字串包含了用空格分隔的三部分GET
,/test/hello.txt
和HTTP/1.1
. 則這個部分的含義就是用 GET 方法來請求伺服器中路徑是/test/hello.txt
的資源, 客戶端使用的 HTTP 版本是 1.1. -
響應報文的起始行
HTTP/1.0 200 OK
和例1類似的, 可以看到這個字串包含了用空格分隔的三部分HTTP/1.0
,200
和OK
. 含義是: 伺服器的 HTTP 版本是 1.0, 對客戶端的請求操作的狀態碼是 200, 表示完成; 同時也用 OK 來說明操作的完成, 便於人類閱讀.
2.2 首部 (header)
就像之前提到的,首部類似於商品說明書,起到對報文附加說明的作用。它是一些名值對的集合. 例如
Content-length: 19
, 表明這個報文的主體部分的長度是 19. 網上專門講首部欄位的文章已經很多了,在此不贅述. 更多更詳細的首部描述見MDN的相應內容. HTTP Headers
首部也可以分為幾種顧名思義的型別,包括:通用首部、請求報文專用首部、響應報文專用首部、實體的專用首部等類別。下面舉例來討論一下這幾種型別:
2.2.1 通用首部
顧名思義,通用首部提供了無論是請求報文還是響應報文都能使用的首部, 在此僅舉幾個例子來說明:
- Date: 日期標誌
- Transfer-Encoding: 對報文的傳輸採用的編碼方式
- Via: 報文經過的中間節點(代理、閘道器)
- Cache-Control: 快取指示
2.2.2 請求報文專用首部
此類首部用來說明客戶端從何而來,具有的能力,希望得到的迴應型別以及最重要的不希望得到的響應的型別等資訊, 以便伺服器根據它的喜好回覆資料.
這類首部種類繁多, 在此僅舉幾個例子:
- Client-IP: 客戶端的 IP 地址
- Host: 接收請求的伺服器的主機名和埠號
- User-Agent: 發起請求的客戶端的程式的名稱
- Accept-Charset: 客戶端期望的字符集
- Accept-Encoding: 客戶端期望的編碼方式
- Accept-Charset: 客戶端期望的字符集
2.2.3 實體專用的首部, 在下一部分討論
2.3 實體 (body)
實體由兩部分組成: 實體首部和實體主體.
2.3.1 實體首部
實體首部提供了實體主體的描述性資訊,包括長度、編碼、型別等等等等. 下面舉幾個例子:
-
Content-Type: 實體主體的型別, 例如 HTML 文件會被標記為 text/html; gif 格式的圖片會被標記為 image/gif. 如果要傳遞的資料型別不止一種, 這種情況可能會出現在 form 表格中, 使用者可能會一次上傳文字和檔案型別的資料, 那麼此時的屬性值會是
Content-Type: multipart/form-data
. 用 multipart 來表示此時傳遞的資料是包含多個主體的. -
Content-length: 實體主體的大小
-
Expires: 實體主體資料的過期時間
-
Content-Encoding: 實體的處理方式,例如壓縮或者重新編碼
2.3.2 實體主體
實體主體就是要傳遞的原始資料~~~(網購的商品的主體)~~~, 比如文件、圖片、音樂等等.
文章只是對 HTTP 報文的一個概覽, 並沒有進行全面的介紹, 而且為了便於部分內容的理解, 做了不是完全恰當的類比.
更多更詳細的內容可見:《HTTP 權威指南》