天天看點

Asp.Net Web API 2第十四課——Content Negotiation(内容協商)

前言

本文描述ASP.NET Web API如何實作内容協商。

HTTP規範(RFC 2616)将内容協商定義為“在有多個表現可用時,為一個給定的響應選擇最佳表現的過程”。在HTTP中内容協商的主要機制是以下請求報頭:

Accept:響應可接收的媒體類型,如“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的内建支援。)

Serialization——序列化

如果Web API控制器傳回一個CLR類型的響應,(請求處理)管線會對傳回值進行序列化,并将其寫入HTTP響應體。

例如,考慮以下控制器動作:

用戶端可能會發送這樣的HTTP請求:

伺服器可能會發送以下響應:

在這個例子中,用戶端請求(指定)了JSON、Javascript、或“任意格式(*/*)”。伺服器以一個Product對象的JSON表示作出了響應。注意,響應中的Content-Type報頭已被設定成“application/json”。

控制器也可以傳回一個HttpResponseMessage對象。為了指定響應體的CLR對象,要調用CreateResponse擴充方法:

該選項讓你能夠對響應細節進行更多的控制。你可以設定狀态碼、添加HTTP報頭等等。

内容協商的工作機制

首先,管線會擷取HttpConfiguration對象的IContentNegotiator服務。它也會得到HttpConfiguration.Formatters集合的媒體格式化器清單。

接着,管線會調用IContentNegotiatior.Negotiate,在其中傳遞:

要序列化的對象類型

媒體格式化器集合

HTTP請求

Negotiate方法傳回兩個資訊片段:

要使用的格式化器

用于響應的媒體類型

如果未找到格式化器,方法傳回null,而用戶端會接收到一個HTTP的406(不可接收的)錯誤。

以下代碼展示了控制器如何才能夠直接調用内容協商:

上述代碼等價于管線的自動完成。

預設的内容協定

DefaultContentNegotiator類提供了IContentNegotiator的預設實作。它使用了幾個選擇格式化器的條件。

首先,格式化器必須能夠對類型進行序列化,這是通過MediaTypeFormatter.CanWriteType來檢驗的。

其次,内容協商器要考查每個格式化器,并評估此格式化器與HTTP請求的比對好壞。為了評估比對情況,内容協商器要對此格式化器考察兩樣東西:

SupportedMediaTypes集合,它含有一個可支援的媒體類型的清單。内容協商器嘗試根據請求的Accept報頭對這個清單進行比對。注意,Accept報頭可以包括範圍。例如,“text/plain”可比對“text/*”或“*/*”

MediaTypeMappings集合,它含有對象一個MediaTypeMapping的對象清單。MediaTypeMapping類提供了一種泛型方式,以比對帶有媒體類型的HTTP請求。例如,它可以将一個自定義的HTTP報頭映射到一個特定的媒體類型。

如果有多個比對,帶有最高品質因子的比對獲勝。例如:

在這個例子中,application/json具有隐含的品質因子1.0,是以它優于application/xml。

如果未找到比對,内容協商器會嘗試比對請求體的媒體類型(有請求體時)。例如,如果請求含有JSON資料,内容協商器會找到JSON格式化器。

如果仍無比對,内容協商器便簡單地撿取能夠對類型進行序列化的第一個格式化器。

選擇字元編碼

 在選擇格式化器之後,内容協商器會選擇最佳字元編碼。通過考察格式化器的SupportedEncodings,并根據請求的報送對其進行比對(如果有)。

繼續閱讀