HTTP

HTTP 內容類型 (Content-Type) & 內容編碼 (Content-Encoding)

(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)

常見的編碼方式有 gzipdeflatebr (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)》中有 2 則留言

發表迴響