ASP.NET Web API中的內容交涉
本文說明 ASP.NET Web API如何實作 ASP.NET 4.x 的內容交涉。
HTTP 規格 (RFC 2616) 將內容交涉定義為「當有多個標記法可用時,為指定回應選取最佳標記法的程式」。HTTP 中內容交涉的主要機制是這些要求標頭:
- 接受: 回應可接受的媒體類型,例如 「application/json」、「application/xml」 或自訂媒體類型,例如 「application/vnd.example+xml」
- Accept-Charset: 哪些字元集是可接受的,例如 UTF-8 或 ISO 8859-1。
- Accept-Encoding: 可接受哪些內容編碼,例如 gzip。
- Accept-Language: 慣用的自然語言,例如 「en-us」。
伺服器也可以查看 HTTP 要求的其他部分。 例如,如果要求包含 X-Requested-With 標頭,表示 AJAX 要求,如果沒有 Accept 標頭,伺服器可能會預設為 JSON。
在本文中,我們將探討 Web API 如何使用 Accept 和 Accept-Charset 標頭。 (目前沒有內建支援 Accept-Encoding 或 Accept-Language.)
序列化
如果 Web API 控制器以 CLR 類型傳回資源,管線會序列化傳回值,並將它寫入 HTTP 回應本文。
例如,請考慮下列控制器動作:
public Product GetProduct(int id)
{
var item = _products.FirstOrDefault(p => p.ID == id);
if (item == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return item;
}
用戶端可能會傳送此 HTTP 要求:
GET http://localhost.:21069/api/products/1 HTTP/1.1
Host: localhost.:21069
Accept: application/json, text/javascript, */*; q=0.01
為了回應,伺服器可能會傳送:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 57
Connection: Close
{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}
在此範例中,用戶端要求 JSON、JAVAscript 或 「anything」 (*/*) 。 伺服器以 物件的 JSON 表示 Product
方式回應。 請注意,回應中的 Content-Type 標頭會設定為 「application/json」。
控制器也可以傳回 HttpResponseMessage 物件。 若要指定回應本文的 CLR 物件,請呼叫 CreateResponse 擴充方法:
public HttpResponseMessage GetProduct(int id)
{
var item = _products.FirstOrDefault(p => p.ID == id);
if (item == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK, item);
}
此選項可讓您更充分掌控回應的詳細資料。 您可以設定狀態碼、新增 HTTP 標頭等等。
序列化資源的 物件稱為 媒體格式器。 媒體格式器衍生自 MediaTypeFormatter 類別。 Web API 提供 XML 和 JSON 的媒體格式器,而且您可以建立自訂格式器來支援其他媒體類型。 如需撰寫自訂格式器的資訊,請參閱 媒體格式器。
內容交涉的運作方式
首先,管線會從HttpConfiguration物件取得IContentNegotiator服務。 它也會從 HttpConfiguration.Formatters 集合取得媒體格式器的清單。
接下來,管線會呼叫 IContentNegotiator.Negotiate,並傳入:
- 要序列化的物件類型
- 媒體格式子的集合
- HTTP 要求
Negotiate方法會傳回兩項資訊:
- 要使用的格式器
- 回應的媒體類型
如果找不到格式器, Negotiate 方法會傳回 null,而用戶端會收到 HTTP 錯誤 406 (無法接受) 。
下列程式碼示範控制器如何直接叫用內容交涉:
public HttpResponseMessage GetProduct(int id)
{
var product = new Product()
{ Id = id, Name = "Gizmo", Category = "Widgets", Price = 1.99M };
IContentNegotiator negotiator = this.Configuration.Services.GetContentNegotiator();
ContentNegotiationResult result = negotiator.Negotiate(
typeof(Product), this.Request, this.Configuration.Formatters);
if (result == null)
{
var response = new HttpResponseMessage(HttpStatusCode.NotAcceptable);
throw new HttpResponseException(response));
}
return new HttpResponseMessage()
{
Content = new ObjectContent<Product>(
product, // What we are serializing
result.Formatter, // The media formatter
result.MediaType.MediaType // The MIME type
)
};
}
此程式碼相當於管線自動執行的動作。
預設內容交涉器
DefaultContentNegotiator類別提供IContentNegotiator的預設實作。 它會使用數個準則來選取格式器。
首先,格式器必須能夠序列化類型。 這是藉由呼叫 MediaTypeFormatter.CanWriteType來驗證。
接下來,內容交涉器會查看每個格式器,並評估其符合 HTTP 要求的方式。 若要評估相符專案,內容交涉器會查看格式器上的兩件事:
- SupportedMediaTypes集合,其中包含支援的媒體類型清單。 內容交涉器會嘗試將此清單與要求 Accept 標頭相符。 請注意,Accept 標頭可以包含範圍。 例如,「text/plain」 是 text/* 或 */*的相符專案。
- MediaTypeMappings集合,其中包含MediaTypeMapping物件的清單。 MediaTypeMapping類別提供一般方法來比對 HTTP 要求與媒體類型。 例如,它可以將自訂 HTTP 標頭對應至特定媒體類型。
如果有多個相符專案,則符合最高品質因素的相符專案。 例如:
Accept: application/json, application/xml; q=0.9, */*; q=0.1
在此範例中,application/json 具有 1.0 的隱含品質因素,因此偏好使用 application/xml。
如果找不到相符專案,內容交涉器會在要求本文的媒體類型上嘗試比對,如果有的話。 例如,如果要求包含 JSON 資料,內容交涉器會尋找 JSON 格式器。
如果仍然沒有相符專案,內容交涉器只會挑選可序列化類型的第一個格式器。
選取字元編碼
選取格式器之後,內容交涉器會藉由查看格式器上的 SupportedEncodings 屬性來選擇最佳字元編碼,並在要求 (是否有任何) 時,將它與要求中的Accept-Charset標頭比對。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應