前言
閱讀本文之前,您也可以到Asp.Net Web API 2 系列導航進行查看 http://www.cnblogs.com/aehyok/p/3446289.html
本教程演示如何在ASP.NET Web API中支持額外的媒體格式。
Internet Media Types——Internet的媒體類型
媒體類型,也叫做MIME類型,標識了一片數據的格式。在HTTP中,媒體類型描述了消息體的格式。一個媒體類型由兩個字符串組成:類型和子類型。例如:
- text/html
- image/png
- application/json
當一條HTTP消息含有一個實體時,Content-Type(內容類型)報頭指定消息體的格式。這是告訴接收器如何解析消息體的內容。
例如,如果一個HTTP響應含有一個PNG圖片,該響應可能會有以下報頭。
HTTP/1.1 200 OK Content-Length: 95267 Content-Type: image/png
當客戶端發送一條請求消息時,它可能包括一個Accept報頭。Accept報頭是告訴服務器,客戶端希望從服務器得到哪種媒體類型。例如:
Accept: text/html,application/xhtml+xml,application/xml
該報頭告訴服務器,客戶端希望得到的是HTML、XHTML,或XML。
在Web API中,媒體類型決定了Web API如何對HTTP消息體進行序列化和反序列化。對於XML、JSON,以及URL編碼的表單數據,已有了內建的支持。而且,通過編寫媒體格式化器(Media Formatter),可以支持額外的媒體類型。
為了創建媒體格式化器,需從以下類進行派生:
- MediaTypeFormatter。這個類使用了異步讀寫方法
- BufferedMediaTypeFormatter。這個類派生於MediaTypeFormatter,但將異步讀寫方法封裝在同步方法之中。
從BufferedMediaTypeFormatter派生要更簡單些,因為沒有異步代碼,但它也意味着在I/O期間可能會阻塞線程。
Creating a Media Formatter——創建媒體格式化器
以下示例演示了一個媒體類型格式化器,它可以將Product對象序列化成一個逗號分隔的值(CSV)格式。該示例使用了Asp.Net Web API 2第二課——CRUD操作 http://www.cnblogs.com/aehyok/p/3434578.html中定義的Product類型。以下是Product對象的定義:
namespace ProductStore.Models { public class Product { public int Id { get; set; } public string Name { get; set; } public string Category { get; set; } public decimal Price { get; set; } } }
為了實現CSV格式化器,要定義一個派生於BufferedMediaTypeFormater的類:
namespace ProductStore.Formatters { using System; using System.Collections.Generic; using System.IO; using System.Net.Http.Formatting; using System.Net.Http.Headers; using ProductStore.Models; public class ProductCsvFormatter : BufferedMediaTypeFormatter { } }
在其構造器中,要添加一個該格式化器所支持的媒體類型。在這個例子中,該格式化器只支持單一的媒體類型:“text/csv”:
public ProductCsvFormatter() { // Add the supported media type. // 添加所支持的媒體類型 SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv")); }
重寫這個CanWriteType方法,以指示該格式化器可以序列化哪種類型:
public override bool CanWriteType(System.Type type) { if (type == typeof(Product)) { return true; } else { Type enumerableType = typeof(IEnumerable<Product>); return enumerableType.IsAssignableFrom(type); } }
在這個例子中,格式化器可以序列化單個Product對象,以及Product對象集合。
相應地,重寫CanReadType方法,以指示該格式化器可以反序列化哪種類型。在此例中,格式化器不支持反序列化,因此該方法簡單地返回false。
protected override bool CanReadType(Type type) { return false; }
最后,重寫WriteToStream方法。通過將一種類型寫成一個流,該方法對該類型進行序列化。如果你的格式化器要支持反序列化,也可以重寫ReadFromStream方法。
public override void WriteToStream( Type type, object value, Stream stream, HttpContentHeaders contentHeaders) { using (var writer = new StreamWriter(stream)) { var products = value as IEnumerable<Product>; if (products != null) { foreach (var product in products) { WriteItem(product, writer); } } else { var singleProduct = value as Product; if (singleProduct == null) { throw new InvalidOperationException("Cannot serialize type"); } WriteItem(singleProduct, writer); } } stream.Close(); } // Helper methods for serializing Products to CSV format. // 將Product序列化成CSV格式的輔助器方法 private void WriteItem(Product product, StreamWriter writer) { writer.WriteLine("{0},{1},{2},{3}", Escape(product.Id), Escape(product.Name), Escape(product.Category), Escape(product.Price)); } static char[] _specialChars = new char[] { ',', '\n', '\r', '"' }; private string Escape(object o) { if (o == null) { return ""; } string field = o.ToString(); if (field.IndexOfAny(_specialChars) != -1) { return String.Format("\"{0}\"", field.Replace("\"", "\"\"")); } else return field; }
Adding the Media Formatter——添加媒體格式化器
為了將媒體類型格式化器添加到Web API管線,要使用HttpConfiguration對象上的Formatters屬性。
public static void ConfigureApis(HttpConfiguration config) { config.Formatters.Add(new ProductCsvFormatter()); }
對於ASP.NET托管,要將這個函數添加到Global.asax文件,並通過Application_Start方法調用它。
protected void Application_Start() { ConfigureApis(GlobalConfiguration.Configuration); // ... }
現在,如果客戶端在Accept報頭指定“text/csv”,則服務器將返回CSV格式的數據。
以下示例使用HttpClient來獲取CSV數據,並將其寫入一個文件:
HttpClient client = new HttpClient(); // Add the Accept header // 添加Accept報頭 client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/csv")); // Get the result and write it to a file. // (Port 9000 is just an example port number.) // 獲取結果並將其寫入文件 // (端口號9000只是一個示例端口號) string result = client.GetStringAsync("http://localhost:9000/api/product/").Result; System.IO.File.WriteAllText("products.csv", result);