- Accept 表示請求方希望的資源類型,或者能解析識別的類型
- Content-Type 表示實際發送的資源類型
這里資源類型通過 MIME types 表示。
Accept
Accept 是瀏覽器發送的請求頭,用於表示想要的資源類型。根據請求的上下文不同,所設置的 Accept 請求頭會相應變化。服務器根據 content negotiation 規則選擇最合適的類型設置 Content-Type
並返回。
例如請求路由頁面時,Chrome 設置 Accept 為
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
對於頁面中的樣式文件 css,其 Accept 為:
Accept: text/css,*/*;q=0.1
可用的值有以下幾種:
<MIME_type>/<MIME_subtype>
,精確指定,示例:text/html
。<MIME_type>/*
, 不限制子類型,比如image/*
會匹配image/png
,image/svg
,image/gif
以及其他任何圖片類型。*/*
任意 MIME 類型。;q= (q-factor weighting)
多種類型組合的情況,通過指定權重(quality value)來表明每種類型的優先級。
Quality value
Header 中逗號分隔的值,每項的權重,或優先級。
比如:
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
表示:
值 | 優先級 |
---|---|
text/html 和 application/xhtml+xml |
1.0 |
application/xml |
0.9 |
*/* |
0.8 |
Content-Type
用來表示資源的類型。某些情況下,瀏覽器會對資源的類型進行嗅探而忽略掉服務器返回的 Content-Type
。如果想強制客戶端使用服務器返回的類型,可加上 X-Content-Type-Options:nosniff
響應頭。
支持的值有:
media-type
,常見的 Content-Type 可 參考這里。charset
,指定資源編碼類型。boundary
, 多個資源實例情況下,指定資源的分界符。比如 form 表單提交時分隔多個表單字段,見后面示例。
一般情況下,包含在由服務器發送給客戶端的響應頭里。但也存在瀏覽器發送給服務器的情況,比如 POST 請求,表單提交這種由瀏覽器向服務器發送數據的情況下。
表單的提交類型
form 表單中,提交的內容類型通過表單的 enctype
屬性來指定。包含兩種:
application/x-www-form-urlencoded
較古老的格式,只支持簡單文本,不支持文件上傳。multipart/form-data
較新,支持文件上傳,尺寸較大的二進制數據等。
一個表單提交示例
通過 koa.js 搭建一個簡單的表單提交示例,以查看 Content-Type。
server.js
const { createReadStream } = require("fs");
const Koa = require("koa");
const app = new Koa();
const router = require("koa-router")();
const koaBody = require("koa-body");
router
.get("/", async ctx => {
ctx.type = "html";
ctx.body = createReadStream("form.html");
})
.post(
"/submit",
koaBody({
multipart: true
}),
ctx => {
console.log("form data is:", ctx.request.body);
ctx.body = JSON.stringify(ctx.request.body);
}
);
app.use(router.routes());
app.listen(3000);
console.log(</span>server started at http://localhost:3000<span class="pl-pds">
);
form.html
<form action="/submit" method="POST" enctype="multipart/form-data">
foo:<input type="text" name="foo" />
bar:<input type="text" name="bar" />
<button type="submit">submit</button>
</form>
訪問頁面並提交后,可在 Chrome DevTools 網絡面板看到,/submit
這個 POST 請求相關的信息:
Request Headers
…
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryNgS8sggyuawQSr8W
…
Form Data
------WebKitFormBoundaryNgS8sggyuawQSr8W Content-Disposition: form-data; name="foo"
1
------WebKitFormBoundaryNgS8sggyuawQSr8W
Content-Disposition: form-data; name="bar"
2
------WebKitFormBoundaryNgS8sggyuawQSr8W--
這里 ------WebKitFormBoundaryNgS8sggyuawQSr8W
便是上面提到的 boundary
,用以分隔表單內容字段。
內容協商/Content Negotiation
前面提到客戶端通過設置 Accept 請求頭設置請求資源的類型,服務器根據 content negotiation 規則返回。
Content negotiation 是這么一種機制,同一 URI可響應多種資源,客戶端可自行決定請求何種資源(譬如文檔的語言,圖片格式,文件編碼類型)。

來自 MDN 展示內容協商流程的圖片
內容協商包含兩種方式:
- 客戶端通過設置請求頭由服務器決定合適的類型進行返回(服務器驅動 )。
- 服務器通過設置響應頭中響應代碼為 300 (
Multiple Choices
)或 406 (Not Acceptable
)作為備用方案(客戶端驅動)。
除了 Accept ,用於主動發起內容協商的請求頭還有:
Accept-Charset
:期望的字符集。Accept-Encoding
:期望的編碼方式。Accept-Language
:期望的語言。
服務器驅動的內容協商
由客戶端發送一組期望的類型,服務器根據自己的算法決定出最合適的類型進行返回,具體實現因服務器類型而異。服務器驅動是最常見的方式,但其也有一些明顯的缺點:
- 由於不是完全了解客戶端的兼容性,服務器的返回有時候存在局限性。相反,客戶端驅動的是服務器返回多個選擇,客戶端根據自己的情況選用最合適的,因為客戶端自己最了解自己支持哪些。
- 關於客戶端的信息在多次請求中會冗余(當然,請求頭冗余的情況在 HTTP/2 中有所緩解),也存在安全隱患(HTTP fingerprinting)。
- 多種類型的資源被返回后,服務端的緩存策略不再那么有效並且實現會變得復雜。
客戶端驅動的內容協商
得到真實資源前多了一次選擇的請求。