(2-2) 資源、表示、URI:
表示 (representation) [rɛprɪzɛnˋteʃən] (確切來說為 資源 的 表示),
旨在透過協議容易傳達的格式 (位元組 bytes),並伴隨 元資料 (metadata),
以反映出 資源「現在」「過去」或「預期」的 『狀態 (state)』。
表示 (representation) 的 元資料 (metadata),通常包含了:
- 內容類型 (Content-Type)
- 內容編碼 (Content-Encoding)
- 內容語言 (Content-Language)
- 內容位置 (Content-Location)
- …
即所謂的 — — 表示表頭欄位 (Representation header fields)。
當訊息 (Message) 存在 酬載主體 (payload body) 時:
(簡單來說,就是 Message Body 有資料啦!)
表示表頭欄位 (Representation header fields) 用來描述:
如何解釋包含在酬載主體中的 表示資料。
目錄
Content-Type (內容類型)
Content-Type 表頭欄位,是指相關 表示 的 媒體類型 (Media Type),
用於定義 資料格式 (data format),以供接收者以相應的方式處理。
對照 (3-1) 訊息格式 (Message Format) 的請求訊息 (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"}
]
}
即此部分 :
Content-Type: application/json; charset=utf-8
媒體類型 (Media Type)
HTTP 的 媒體類型 (Media Type),是由 多用途網際網路郵件擴展 (MIME) 衍生而來,
因 Web 與 Email 特性不同,兩者存在些許 差異。
媒體類型 由 類型/次要類型 以及 可選的參數 (parameter) 所構成,
其中,最常見的參數是 charset (字元集合) :
type/subtype[; parameter]
[註]:
傳統的預設 charset 為 ISO-8859-1,
已於 2014 年的 HTTP/1.1 修訂中移除。
主要類型 (type) 通常分為 文字 (text)、影像 (image)、
音訊 (audio)、影片 (video) 及 應用 (application) 五大類,常見如:
- text/html
- text/html; charset=utf-8
- text/plain;
- text/css
- image/jpeg
- mage/png
- audio/mpeg
- audio/ogg
- video/quicktime
- video/mp4
- application/octet-stream
- application/x-www-form-urlencoded;
- application/x-www-form-urlencoded; charset=utf-8
- application/json
- application/xml
其中「application/x-www-form-urlencoded」or「application/x-www-form-urlencoded; charset=utf-8」,普遍用於 HTML 中的 POST 表單 (e.g., 提交帳號密碼),是 類似百分比編碼 的 鍵值對 形式,
例如:「name=%E5%8B%9D&password=9487」。
(‘勝’ 的百分比編碼為 %E5%8B%9D)
另外還有兩種複合類型 多部分 (multipart)、訊息 (message) 與其他延伸格式 :
multipart/form-data; boundary=----9487QQ
message/http
其中「multipart/form-data; boundary=—-XXXXXX」(boundary 值不重要),
普遍用於 HTML POST 表單 中的 上傳檔案。
訊息處理
無論是 請求 或 回應,若訊息中存在 酬載主體,
發送者 皆應該 生成 Content-Type 表頭欄位。
反之,若不存在則 (訊息) 接收者 可能檢查資料格式,
或以二進制的 「application/octet-stream」處理。
實務上,接收者 可能無視 Content-Type 表頭欄位,
並以特定 媒體類型 複寫,
一個傳統的例子是,將 影像 以 純文字 來處理 (並不建議)。
完整的 mediatype 詳見 IANA。
Content-Encoding (內容編碼)
Content-Encoding 表頭欄位,指出 應用於 表示 的編碼方式,
以指示 (訊息) 接收方,使用相應的解碼機制來獲取資料,
主要用於壓縮資料,以增進傳輸效率。
Ilya Grigorik :
壓縮就是使用更少的位元對資訊進行編碼的過程,
GZIP 對於文字資產的壓縮效果最好:CSS、JavaScript、HTML。
通常能將傳輸的數據大小減少一半,甚至更多 !
(需視內容而定)
這是一約 89.73 kb 的純文字組成之網頁:
經 gzip 壓縮後,僅剩 24 kb,壓縮率高達 73% !
只要 訊息接收方 支援,沒什麼理由不使用內容編碼。
壓縮 (Compression)
常見的編碼方式有 gzip、deflate、br (Brotli) 及 compress,
如果 表示 (representation) 應用了一或多個編碼,
則 (訊息) 發送方 必須生成 Content-Encoding 表頭欄位,並按照 使用順序 列出編碼方式 (coding)。
何謂 內容 (Content) ?
若訊息經過 內容編碼 (e.g., gzip 壓縮),則 Content-Type 指的是:
解碼後 (decoded) 的資料
然而,這使「表示」是『已編碼』還是『解碼』資料 混肴不清,
而被視為早期的 設計錯誤,且沿用至今。
其實理想的作法是將 Content-Type 視為「最外層」的媒體類型 (i.e., 尚未解碼之資料),
但一切都太晚了 😂,Content-Type 需視為特例之一。
也就是:
得將 Content-Type 視為 — — 已解碼之資料的類型。
因此,HTTP 標準所說的 表示資料 (Representation Data) 或不斷提及的 內容 (Content),
是 兩層的 (two-layer) 有序編碼模型 — — 尚未解碼 (如果有的話) 之 資料 :
representation-data := Content-Encoding( Content-Type( bits ) )
請求內容編碼
許多人誤解 內容編碼 (Content-Encoding) 僅用於 回應,
事實上,Content-Encoding 能用於請求 訊息 !
然而,一個非常重要的前提是:
你得確保 Server/Origin Server 擁有處理此編碼之能力!
JS
gzip 的基礎是 DEFLATE,而 zlib 是實作 DEFLATE 的函式庫,
我們可以使用實作 zlib 的 — — pako JS,
輕鬆為 JavaScript 請求酬載進行編碼 !
例如:
// 準備 資料酬載 (Payload)
var data = {email: "jason@gg", password: "9487"};
var encodedData = encodeFormData(data);
var payloadLen = encodedData.length;
var input = new Uint8Array(payloadLen);
for (var i = 0; i < payloadLen; i++) {
input[i] = encodedData.charCodeAt(i);
}
var output = pako.gzip(input);
千萬別忘記 表頭欄位:
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
headers.append('Content-Encoding', 'gzip');
可參考 JavaScript 發送 HTTP 請求 — (III) Fetch API。
Java
Java 則能以原生的 GZIPOutputStream
進行壓縮:
private static byte[] compress(String str) throws UnsupportedEncodingException {
byte[] data = str.getBytes("UTF-8");
byte[] result = new byte[0];
try (ByteArrayOutputStream bs = new ByteArrayOutputStream()) {
try (GZIPOutputStream gs = new GZIPOutputStream(bs)) {
gs.write(data);
}
result = bs.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
shell script
能使用 gzip 指令進行壓縮,並以 curl 指令送出請求 :
## Payload
echo "email=jason%40gg&password=9487" | gzip -c > payload.gz;
## Request
curl --data-binary @payload.gz
-X "POST" "https://reqres.in/api/register"
-H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8"
-H "Content-Encoding: gzip"
Result:
{"token":"QpwL5tke4Pnpja7X"}
相關指令用法,可參考 鳥哥的 Linux 私房菜:
第十章、認識與學習BASH 與 第八章、檔案與檔案系統的壓縮。
回應內容編碼
Apache:使用 mod_deflate
Nginx:使用 HttpGzipModule
IIS:設定 HTTP 壓縮功能
詳見 PageSpeed Insights。
Content-Language (內容語言)
Content-Language 表頭欄位,大略描述 表示 預期受眾的 自然語言 (natural language),
主要目的是允許使用者能根據自己的首選語言來識別和區分 表示,
針對多個受眾群體,可能列出多種語言。
例如:
Content-Language: en
Content-Language: en-CA
Content-Language: zh
Content-Language: zh-Hant
Content-Language: zh-TW
Content-Language: mi, en
其中 zh-Hant 為 繁體中文:
‘zh’ (Chinese)
‘Hant’ (Han script traditional variant).
‘en’ (English)
‘en-CA’ = the variety of English as communicated in Canada).
詳見 MDN。
Content-Location (內容位置)
Content-Location 表頭欄位 是指與回應 表示 相對應的最具體資源,
可能是備用 URI,也可能是更具體的位址,
主要用途是做為 內容協商 的資源 URI。
值得注意的是,另一表頭欄位: Location (位置),
是回應訊息中,引用與 回應相關 的特定資源 之 URI,
主要用於 — — 重新導向。
有些導致資源狀態改變的 請求/回應 (e.g., 以 POST 方法新增資源後的 201 Created 回應),
Content-Location 表頭欄位 與 Location (位置) 意義相近,皆指向新建立的資源,
否則,兩者大多時候意義不同。
回應可能 同時 包含 Location 和 Content-Location 表頭欄位。
詳見 MDN。
在《HTTP 內容類型 (Content-Type) & 內容編碼 (Content-Encoding)》中有 4 則留言
> gzip 的基礎是 DEFLATE,而 zlib 是實作 DELFATE 的函式庫,
這邊打錯了,應該是DEFLATE而不是DELFATE
內容太詳盡啦!好文!
太感謝了!😭 已修正
媒體類型 (Media Type) 段落中的 “mage/png” 是不是錯字了?正確應該是 “image/png”
太推了 感謝~