Skip to main content

[HTTP] Content Type

本文非原創,以下是參考資料:


覺得自己一直都沒有很清楚 HTTP Content-Type 這塊,
每次在傳 POST api 資料時,都不知道為什麼要傳這類型的 Content-type,
所以決定來整理一下

什麼是 Content-type ?

簡介

HTTP 向接收方說明傳輸資料的的媒體類型 (media type),
媒體即是傳送訊息的一個傳輸的管道或形式,像是 文字(text)、圖片(image)、影片(video)、音樂(audio)、應用程式(application)等等,

在瀏覽器中,response 會根據 Content-type 決定顯示的樣子,
若在 response header 中聲明了 Content-type: image/jpeg,則資料會以圖片方式呈現
若沒有標明,則會預設帶 Content-type: text/html,以文字方式呈現

<?php
header('Content-type: image/jpeg');
echo file_get_contents("https://media.geeksforgeeks.org/wp-content/uploads/geeksforgeeks.png");
?>

輸出結果:
Geeks for Geeks in picture form


<?php
// Without Content-type, will be text/plain
echo file_get_contents("https://media.geeksforgeeks.org/wp-content/uploads/geeksforgeeks.png");
?>

輸出結果:

?PNG  IHDRX??'?iCCPsRGB IEC61966-2.1(?u??+DQ??3????????????63??P????H?U????l??RDJV???9oF?
$sn????{N???pZ??^?d?Z(p?E?]??h??QEW?f??T??{, f???????????z?aE??????y???6%]>vkrA?;S?????d??M?
¡?6???`%?????&???Q-Z?j????BSZo?a???}N ?._u {??#??N?g?{-bKGD??????? pHYs.#.#x??vtIME?4_?X
IDATx??w?U??????MB$??$@@? 2t?"EDa???"? C?*C????Hq?ja??w ????????L{??}?}??w?;??{???{.4, ???j???
q10??_??h2]`P??:^?5??@?W?=????????XY??? w.??9??`z?1?!V??B????XM~^?|?1?qm???(?h??C?OV?js{e?+
L?b?{%?@`?+:sQ?@?


規範

Content-Type 包含以下 3 個部分,

  • media type:資料的 media type,以 MIME 為標準
  • charset:數據用何種字符集
  • boundary:資料分格符號,當有複合型 media 時 (multipart / message) (RFC2046 page 5)
    ,此部分是必須的,用來分隔不同的資料;由 1 到 70 個字元組成,瀏覽器會自動加上,不用自己加
MIME

HTTP 可使用的 media type 是參照 MIME (Multipurpose Internet Mail Extensions) 而來,
MIME 是一種標準,因為早期的電子郵件只能用文字傳輸,而當後續的電子郵件開始需要傳輸圖片、影片、音樂等等,
就需要一種標準來表示這些媒體類型


POST 請求常用的 media type

GET 和 POST 是我們最常用的兩個 HTTP 請求方法。對於 GET請求,需要傳遞的數據比較簡單,我們通常使用 queryString 的方式傳遞,例如 https://test.com/api?a=1&b=2
Content-Type 的值就沒有太大作用了。

...
The Content-Type header field specifies the nature of the data in the
body of an entity by giving media type and subtype identifiers, ...
...


但對於 POST 請求,Content-Type 的值就非常重要了,需要根據不同場景做不同選擇,我們依常見的需求介紹:


傳輸 JSON 檔

1. application/json

Content-type:  application/json

JSON 本身是文字,為什麼不用 text/plain 呢?

根據 RFC 8259 section 12,因為 text 中可能可以包含可執行的程式碼,
而 JSON 本身也是 JavaScript 的子集合(僅沒有 assignment & invocation),
這會造成傳輸資料時資安上的風險,我們可以在傳輸中途惡意的程式碼,類似使用 eval() 等程式碼轉換

因此,我們需要特別的傳輸方式轉換,這時候就需要 application/json

application MIME type (RFC 2046)

application 為除了 text, image, audio, video 之外的資料
通常是一些

  1. 未編譯的 binary data
  2. 一些會被用在網路上的應用程式的資料

JSON 就符合第 2 個條件,且又不希望被當成 text 傳輸,因此 application 是最適合的主型別


傳輸 Form data

1. application/x-www-form-urlencoded

Content-type: application/x-www-form-urlencoded

Form 默認的編碼方式,使用該值時,提交表單時內容必須經過如下規則編碼:

空格轉換為 “+” 號;非字母數字的其它字符轉換為類似於“%E0”的兩位 16 進制表示的 ASCII 碼;換行符被轉換為“CR LF”; 數據項名稱和數據值以“=”號分割,數據項與數據項之間以“&”分割;

範例:

<form action="https://xxx.com/api/submit" method="post">
<input type="text" name="name" value="Javon Yan">
<input type="text" name="age" value="18">
<button type="submit">Submit</button>
</form>

Request Header & Body

// Request Header 部分省略
POST /foo HTTP/1.1
Content-Length: 37
content-type: application/x-www-form-urlencoded

// Body
name=Javon+Yan&age=18


2. multipart/form-data

Content-type: multipart/form-data

對於二進制文件或者非 ASCII 字符的傳輸,application/x-www-form-urlencoded 是低效的。
對於包含文件、二進制數據、非 ASCII 字符的內容,應該使用 multipart/form-data。
multipart/form-data 的請求體包含多個部分,需要通過 boundary 字符分割。
以下示例中,Form 表單 enctype 設置為 multipart/form-data ,請求頭及請求體如下所示,瀏覽器自動生成隨機的 boundary 並添加在請求頭 Content-Type 中,請求體也根據生成的 boundary 分割各個字段的數據。 示例:

<form action="https://xxx.com/api/submit" method="post" enctype="multipart/form-data">
<input type="text" name="name" value="Javon Yan">
<input type="text" name="age" value="18">
<input type="file" name="file">
<button type="submit">Submit</button>
</form>

Request Header & Body

// Request Header 部分省略
POST /foo HTTP/1.1
Content-Length: 10240
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryujecLxDFPt6acCab

// Body
------WebKitFormBoundaryujecLxDFPt6acCab
Content-Disposition: form-data; name="name"

Javon Yan
------WebKitFormBoundaryujecLxDFPt6acCab
Content-Disposition: form-data; name="age"

18
------WebKitFormBoundaryujecLxDFPt6acCab
Content-Disposition: form-data; name="file"; filename="avatar.png"
Content-Type: image/png

... (png binary data) ....
------WebKitFormBoundaryujecLxDFPt6acCab--


傳輸 binary data

1. application/octet-stream

Content-type: application/octet-stream

用於傳輸二進制數據。可用於上傳文件的場景。在 Postman 中,還可以看到 "binary" 這一類型,指的就是一些二進制文件類型。
如 application/pdf,指定了特定二進制文件的 MIME 類型。就像對於text文件類型若沒有特定的子類型(subtype),就使用 text/plain。
類似的,二進制文件沒有特定或已知的 subtype,就使用 application/octet-stream,這是應用程序文件的默認值,一般很少直接使用。
對於 application/octet-stream,只能提交二進制,而且只能提交一個二進制,如果提交文件的話,只能提交一個文件,後台接收參數只能有一個,而且只能是流(或者字節數組)。
很多 web 服務器使用默認的 application/octet-stream 來發送未知類型。出於一些安全原因,對於這些資源瀏覽器不允許設置一些自定義默認操作,導致用戶必須存儲到本地以使用。一般來說,設置正確的MIME類型很重要。