HTTP 透過 統一介面 (uniform interface) 使:
Client 送出 請求 (Request),
Server 送出 回應 (Response)。
訊息 (Message) !
目錄
訊息格式 (Message Format)
訊息 (Message),又譯為 報文、信息、消息,
是 HTTP/1.1 的最小傳輸單元。
[註]:
HTTP/2 的最小傳輸單元為 訊息框 (Frame),
而 多個訊息框 組合 為一個訊息。
所有訊息都是由:
- 起始行 (start-line) 開始
- 0 或多個 表頭欄位 (header-field) + CRLF
[合稱為 表頭 (headers) 或是 表頭部分 (header section)] - 再加上一個 CRLF
- 最後是 可選的 (optional) 訊息主體 (message-body)
HTTP-message = start-line
*( header-field CRLF )
CRLF
[ message-body ]
請求訊息 (request message) 範例 :
(顏色對應上方格式)
POST /?id=1 HTTP/1.1
Host: echo.paw.cloud
Content-Type: application/json; charset=utf-8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0
Connection: close
Content-Length: 136
{
"status": "ok",
"extended": true,
"results": [
{"value": 0, "type": "int64"},
{"value": 1.0e+3, "type": "decimal"}
]
}
回應訊息 (response message) 範例 :
(顏色對應上方格式)
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Date: Sat, 18 Feb 2017 00:01:57 GMT
Server: nginx/1.11.8
transfer-encoding: chunked
Connection: Close
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>echo</title>
....略
請求/回應 訊息,語法上格式相同,
而 內容 與 訊息主體 (message body) 的規則不同。
CRLF
(carriage return followed by line feed)
HTTP-message = start-line
*( header-field CRLF )
CRLF <-- -- 在這!
[ message-body ]
這裡先插個隊 ! 因為接下來會一直用到它 😄。
CRLF (carriage return followed by line feed),
是網際網路 (Internet) 嚴謹的 換行 (newline) 標準 ,
也就是鼎鼎大名的:
\r\n
CR = 回車 (Carriage Return),又稱 歸位,
源於傳統的電傳印表機 (TTY),用於回到一行字的起頭,而非換行。
許多語言表示為: \r
在 Unicode 中為 0x0D (十進制的 13)。
(顯然跟我們鍵盤上的 Enter (return) 意義已不相同)
LF = 換行 (Line Feed),
許多語言表示為: \n
在 Unicode 中為 0x0A (十進制的 10)。
雖然多數語言 \n 就能做到換行,
規範上,仍以 \r\n 為主。
對應訊息範例的:
CRLF,是接續在 表頭 (Header) 之後的 必要 部分。
然而,許多粗心的程設設計師,在沒有 訊息主體 (message body) 部分時,
時常忽略了 CRLF,是實作上需注意的地方。
起始行 (start-line)
HTTP-message = start-line <-- -- 在這!
*( header-field CRLF )
CRLF
[ message-body ]
起始行 (start-line),做為訊息的開始,
是 請求訊息 與 回應訊息 內容上最大的差異,
因應 請求、回應,分別稱為:
請求行 request-line
狀態行 status-line
請求行 (request-line)
請求行 (request-line) 依序包含:
方法 (method)、空白 (space, SP)、
請求目標 (request-target)、再一個 空白 (space, SP)、
HTTP 版本 (HTTP-version)、最後是已介紹過的 CRLF (carriage return followed by line feed)。
對照上方的請求訊息 (request message) 範例,
POST /?id=1 HTTP/1.1
Host: echo.paw.cloud
Content-Type: application/json; charset=utf-8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0
Connection: close
Content-Length: 136
{
"status": "ok",
"extended": true,
"results": [
{"value": 0, "type": "int64"},
{"value": 1.0e+3, "type": "decimal"}
]
}
即此部分 :
POST /task?id=1 HTTP/1.1
1. 方法 (method)
方法 (method),對應請求範例的 POST
全稱 請求方法 (request method)。
(2-2) 資源、表示、URI 提及:
URI 對映的資源 是 『名詞』,
而操作這些資源的『動詞』: GET、POST、PUT、DELETE… 就稱為 方法 (method)。
常聽到的 GET 請求、POST 請求…,就是由此欄位所決定 !
因此可知,它們本質上是相同的東西 (i.e., 訊息),只是規範與使用方式不同。
Ex:
https://example.org/apple
這個 URI 有蘋果 🍎 這個資源,
使用 『GET』 方法,就是 『給』我蘋果🍎 啦!
(4-2) GET vs. POST 有詳盡的說明 😉
需注意:
『 方法 』是 區分大小寫的 ( case-sensitive ),需使用大寫英文。
2. 空白 (space, SP)
Photo by Apple Inc.
3. 請求目標 (request-target)
對應請求範例的 /task?id=1
請求目標 (request-target),總共有四種形式,
採用何種形式,取決於使用的 請求方法 (Request Method) 與 代理 (proxy) 方式。
礙於篇幅,這裡只介紹 原始形式 (origin-form),
可以在 (5-1) 訊息路由 (Message Routing) 查看更多 請求目標 (request-target)。
原始形式 (origin-form),是最常見的形式:
origin-form = absolute-path [ "?" query ]
http(s) URI 格式 =
"http(s):" "//" authority path-abempty [ "?" query ] [ "#" fragment ]
原始形式 origin-form 類似 path-abempty + [ "?" query ] 。
不同之處在於:
不能為 空路徑,必須以 " / " 開始 !
例如訪問:
https://echo.paw.cloud/?id=1
原始形式 (origin-form) 為:
/?id=1
也就是範例 /task?id=1 的由來啦 !
如果把 query 拿掉呢?
https://echo.paw.cloud
原始形式 (origin-form)為:
/
記得要有 『 / 』!!
當 Client 直接 將訊息發送給 Origin Server,
(且不是使用 CONNECT 請求 或 伺服器整體的 OPTIONS 請求)
必須使用 原始形式 origin-form 作為 請求目標 (request-target)。
4. 空白 (space, SP)
Photo by Apple Inc.
5. HTTP 版本 (HTTP-version)
對應請求範例的 HTTP/1.1
格式為:
HTTP-name "/" DIGIT "." DIGIT
Ex:
HTTP/1.1、HTTP/1.0
注意:
『 HTTP 』需為大寫
6. CRLF
介紹過啦 ! 以一個『空行』代表請求行的結束。
狀態行 (status-line)
狀態行 (status-line) 依序包含:
HTTP 版本 (HTTP-version)、空白 (space, SP)、
狀態碼 (status-code)、再一個 空白 (space, SP)、
原因短語 (reason-phrase)、以及 CRLF (carriage return followed by line feed)。
對照上方的回應訊息 (response message) 範例,
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Date: Sat, 18 Feb 2017 00:01:57 GMT
Server: nginx/1.11.8
transfer-encoding: chunked
Connection: Close
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>echo</title>
....略
即此部分 :
HTTP/1.1 200 OK
HTTP 版本 (HTTP-version)、SP、CRLF,不再累述。
狀態碼 與 原因短語
(status-code & reason-phrase):
對應回應範例的 200 與 OK
(4-6) 回應狀態碼 (Response Status Codes) 將有詳盡的介紹。
表頭 (header)
HTTP-message = start-line
*( header-field CRLF ) <-- -- 在這!
CRLF
[ message-body ]
表頭 (header),又譯為 首部、檔頭、標頭,
是由 0 或多個 表頭欄位 (header-field) + CRLF 組合而成。
類似程式語言 函式/方法的 參數 (parameter)。
例如,你對一服務生指定 目標資源 — — 牛排,
而 要幾分熟、幾份餐具、點心什麼時候上…,
這種 附加的重要資訊,即為 表頭 (header)。
加上 起始行 (start-line) 部分,
即 (1-3) 統一介面 所述之 介面約束 — — 自我描述的訊息 (self-descriptive messages)。
對照上方的請求訊息 (request message) 範例,
POST /?id=1 HTTP/1.1
Host: echo.paw.cloud
Content-Type: application/json; charset=utf-8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0
Connection: close
Content-Length: 136
{
"status": "ok",
"extended": true,
"results": [
{"value": 0, "type": "int64"},
{"value": 1.0e+3, "type": "decimal"}
]
}
即此部分 :
Host: echo.paw.cloud
Content-Type: application/json; charset=utf-8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0
Connection: close
Content-Length: 136
表頭欄位 (header-field)
單個 表頭欄位 (header-field) 的組成 :
- 一個 不區分大小寫的 欄位名稱 (field-name) — — 習慣上仍以首字大寫為主
- 緊接一個 冒號 (colon ":")
- 可選的空白 (optional trailing whitespace, OWS)
- 欄位值 (field-value)
- 可選的空白 (optional trailing whitespace, OWS)
header-field = field-name ":" OWS field-value OWS
欄位名稱 (field-name) 與 冒號 (colon ":") 之間不可有空白!
多個欄位值之間,可用 空白 或 分隔符號 分隔
分隔符號 (delimiter) :
" ( ) , / : ; < = > ? @ [ \ ] { }
也可以在表頭中,以 括號 補充說明 — — 註釋 (Comment) :
(Macintosh; Intel Mac OS X 10.12; rv:53.0)
一個訊息,沒有限制欄位的數量,
表頭名稱的引入,也沒有任何限制。
因此,有許多常見的 非標準表頭欄位。
所有正式定義的訊息表頭,都會在 網際網路號碼分配局 (IANA) 註冊。
ASCII
從上例不難看出,排除 分隔符號 以外,
表頭欄位 (header-field) 是基於拉丁字母的 ASCII 碼。
而 HTTP/2 則進一步,將表頭欄位壓縮為 二進制 的有序串列,
稱為 表頭區塊片段 (Header Block Fragment)。
這也是 HTTP/1.1 和 HTTP/2 最大的差異 之處 !
隨著網頁已成長到需要數十到數百個請求,
HTTP/1.1 未經壓縮的表頭,
使訊息頻繁地出現冗餘的欄位,不必要地消耗頻寬並造成延遲。
HTTP/2 則以 表頭壓縮 (Header Compression),
大幅地增進效能與擴充性 !
重複欄位 (Duplicate Header)
發送方 (sender) 不得在訊息中,生成相同的表頭欄位名稱。
除非:
- 整個個欄位值,以 逗號分隔 (comma-separated) 列表呈現
- 表頭欄位是公認的例外 (i.e., Set-Cookie 表頭欄位)
此時,接收方 (recipient) 可以將多個相同欄位名稱的值,
照順序以 逗號分隔 (comma-separated) 組合在一起,而不改變訊息的語意。
因此:
接收 具有相同欄位 名稱的訊息時,順序 是很重要的,
代理 (proxy) 不得在轉發訊息時,更改這些欄位值的順序。
Set-Cookie 欄位,是一個公認的特殊情形,
實際上,他時常在 回應訊息中 出現多次,
並非使用逗號分隔,也無法組合成單個表頭欄位,
接收方 (recipient) 必須將 Set-Cookie 做為特殊情形處理。
欄位順序 (Field Order)
除了上一節會提到的名稱重複問題,
表頭欄位的順序,對於接收方並不是太重要。
然而,優良的做法仍是以 『控制』欄位為首,
(e.g., 請求訊息 優先使用 Host 欄位,回應訊息 優先使用 Date 欄位。)
這樣的好處是,接收方可以儘早的決定,是否處理訊息。
注意事項:
在確實接收完整的表頭欄位前,
Server 不應該將請求應用於目標資源上。
因為後續有可能出現條件請求、驗證憑證、故意誤導的重複欄位…,
這些影響請求處理的表頭欄位。
訊息主體 (Message Body)
HTTP-message = start-line
*( header-field CRLF )
CRLF
[ message-body ] <-- -- 在這!
訊息主體 (message body),又譯為 訊息體、報文主體,
就像包裹的箱子,是 訊息 (message) 中 乘載 資料的地方。
能乘載任意格式的資料 (位元組):
對照上方的請求訊息 (request message) 範例,
POST /?id=1 HTTP/1.1
Host: echo.paw.cloud
Content-Type: application/json; charset=utf-8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0
Connection: close
Content-Length: 136
{
"status": "ok",
"extended": true,
"results": [
{"value": 0, "type": "int64"},
{"value": 1.0e+3, "type": "decimal"}
]
}
即此部分:
{
"status": "ok",
"extended": true,
"results": [
{"value": 0, "type": "int64"},
{"value": 1.0e+3, "type": "decimal"}
]
}
酬載 (payload)
資料酬載 (payload) 或稱 酬載、有效載荷,
是傳輸時 實際預期的資料,可能是 完整 or 部分 的 表示 (representation)。
也就是範例中的 :
{
"status": "ok",
"extended": true,
"results": [
{"value": 0, "type": "int64"},
{"value": 1.0e+3, "type": "decimal"}
]
}
因此,我們稱 訊息主體 (Message Body) 包含了 (contain) 酬載主體/有效載體 (payload body)。
與 表示 (representation) 不同的是,
酬載 (payload) 語意上 不考慮資料的內容是何種 格式、語言、編碼…,
強調的是傳輸的 資料 位元組 (octet) 本身,
更多差異,詳見 (3-4) 酬載 — Content-Length, Transfer-Encoding…。
描述酬載的 元資料 (metadata) — — 酬載表頭欄位 (payload header fields):
- 傳輸編碼 (Transfer-Encoding)
- 內容長度 (Content-Type)
- 內容範圍 (Content-Range)
- 拖曳 (Trailer)
- …
在《HTTP/1.1 — 訊息格式 (Message Format)》中有 4 則留言
好文
文章好漂亮,受益良多
很用心的一篇文章!!
說明的很詳細,讚!