標籤:
前言
閱讀本文之前,您也可以到Asp.Net Web API 2 系列導航進行查看 http://www.cnblogs.com/aehyok/p/3446289.html
本文描述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響應體。
例如,考慮以下控制器動作:
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.1Host: localhost.:21069Accept: application/json, text/javascript, */*; q=0.01
伺服器可能會發送以下響應:
HTTP/1.1 200 OKContent-Type: application/json; charset=utf-8Content-Length: 57Connection: Close{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}
在這個例子中,用戶端請求(指定)了JSON、Javascript、或“任意格式(*/*)”。伺服器以一個Product對象的JSON表示作出了響應。注意,響應中的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, product);}
該選項讓你能夠對響應細節進行更多的控制。你可以設定狀態代碼、添加HTTP前序等等。
對資源進行序列化的對象叫做媒體格式化器。媒體格式化器派生於MediaTypeFormatter類。Web API提供了XML和JSON的媒體格式化器,因而你可以建立自訂的格式化器,以支援其它媒體類型。更多關於編寫自訂格式化器的資訊 http://www.cnblogs.com/aehyok/p/3460164.html。
內容協商的工作機制
首先,管線會擷取HttpConfiguration對象的IContentNegotiator服務。它也會得到HttpConfiguration.Formatters集合的媒體格式化器列表。
接著,管線會調用IContentNegotiatior.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(MIME類型) ) };}
上述代碼等價於管線的自動完成。
預設的內容協定
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,並根據請求的報送對其進行匹配(如果有)。
Asp.Net Web API 2第十四課——Content Negotiation(內容協商)