淺析ASP.Net Web API的Formatter

來源:互聯網
上載者:User
文章目錄
  • (1)缺乏不帶參數的建構函式
  • (2)類型沒有被定義為public

本文的內容很多來自:http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization

我狗尾續貂地添加些額外的說明與見解,或許對英文不太好的朋友有些用。

ASP.net的Web API和傳統MVC網站有個很大的不同就是多了Formatter(格式化器),其實Formatter並不是什麼新鮮東西,我覺得它只是另一種 ModelBinder 方法,簡單地說,就是HTTP的資料到.Net對象的關係。MVC的Model Binding做過MVC網站的人應該都很熟悉了,就是嘗試從HTTP請求中找到一些“key=value”的索引值對,根據一些約定,匹配到Model的屬性或Action參數上去,如果Model中有個實值型別(例如int)的屬性,而HTTP請求中又沒有,那麼會出現ArgumentException異常,並預設顯示出大家非常熟悉的YSOD(Yellow Screen Of Death)。

我想微軟弄格式化器的原因是想讓資料繫結具有更強的延展性,想像一下:你可以用格式化器自訂日期的輸出格式;通過格式化器,將一個字串轉變為一張小png圖片輸出;還有更清晰和明確地定義資料等等。

一、有哪些Formatter?

建立一個預設的MVC4 Web API工程,在WebApiConfig中加入:

    foreach (var fmt in config.Formatters)    {        System.Diagnostics.Debug.WriteLine(fmt.GetType());    }

預設情況下,能在Output視窗下看到:

System.Net.Http.Formatting.JsonMediaTypeFormatter
System.Net.Http.Formatting.XmlMediaTypeFormatter
System.Net.Http.Formatting.FormUrlEncodedMediaTypeFormatter
System.Web.Http.ModelBinding.JQueryMvcFormUrlEncodedFormatter

能看到這些格式化器,一眼就看出來,JsonMediaTypeFormatter是用來負責JSON的序列化/還原序列化的,XmlMediaTypeFormatter是用來負責XML的序列化/還原序列化的,FormUrlEncodedMediaTypeFormatter用來處理URL帶的請求參數,JQueryMvcFormUrlEncodedFormatter的處理內容應該跟表單資料相關。

二、Web API是怎麼處理XML的?

一開始我以為Web API是用System.Xml.XmlSerializer來處理XML,但後來發現不是(預設不是)。很簡單的證據就是:

XmlSerializer預設不能序列化IEnumerable,它會報錯說IEnumerable是介面,如果你要序列化,恐怕得自行實現IXmlSerializable介面;但預設情況下,Web API能輕鬆地將IEnumerable<Order>這樣的類型序列化為XML並返回給用戶端。那Web API預設用了什麼XML序列化器呢?——DataContractSerializer。

建立一個最簡單的控制台程式,然後用下面的代碼測試一下:

    IEnumerable<string> testobj = new string[] { "aaa", "bbb", "ccc" };    DataContractSerializer ser = new DataContractSerializer(testobj.GetType());    ser.WriteObject(Console.OpenStandardOutput(), testobj);    //Error    //XmlSerializer ser = new XmlSerializer(test.GetType());    //ser.Serialize(Console.Out, test);

我個人覺得使用DataContractSerializer更好,也就是Web API預設的設定,很明顯,能序列化IEnumerable對我們來說太必要了。但有些習慣了使用XmlSerializer相關序列化特性(如[XmlAttribute],[XmlRoot],[XmlElement]之類)的人會比較喜歡XmlSerializer,要這樣做也很簡單,只需要在WebApiConfig.cs中加入:

config.Formatters.XmlFormatter.UseXmlSerializer = true;

DataContractSerializer的序列化特性其實也很豐富,WCF的相關文章會有很詳盡的描述,這裡就不展開了。

三、用XML還是JSON?

Web API會自動選擇,選擇的依據是請求的報文的HTTP Header:

Accept: application/json
Content-type: application/json

Content-type表示請求的body中的資料類型,是JSON還是XML,還是圖片或者別的;Accept表示這個請求所期待得到的資料類型。上面的請求表示請求報文body中的資料為JSON,所期待的返回的資料類型也是JSON。如果要XML,那麼很簡單,把application/json改為application/xml即可。

我通過實驗發現,Web API還有一條規則:如果請求的的資料類型由於某些原因無法正常得到,那麼嘗試以別的資料類型來返回。比如請求XML,而XML在序列化的時候出現了問題,那麼Web API會嘗試用JSON返回資料。

四、序列化為XML失敗的可能原因

XML和JSON,我更喜歡JSON,因為簡潔,XML包含了太多的標籤、Schema以及namespace,很容易讓人眼花繚亂,但有些用戶端處理XML更為便利,所以我們還是要考慮一下XML的序列化問題。

(1)缺乏不帶參數的建構函式

如果你的class缺乏不帶參數的建構函式,那麼序列化成XML的時候就會報錯,我一開始也想不明白為什麼會這樣,要個不帶參數建構函式幹什麼呢?直接把裡面該序列化的東西序列化好不就OK了嗎?大家花一分鐘時間想想看啊!反正我想不出來為什麼,你要是能想出來的話說明你比我聰明,呵呵……OK,言歸正傳了,原因其實很簡單:還原序列化!

(2)類型沒有被定義為public

這個限制的原因可能是:序列化器認為,對非public的類型序列化會破壞資料的封裝性。

五、JSON的序列化器以及控制時間日期的格式

MVC4的正式版跟之前的Beta版有不少差異,其中一個就是把JSON的序列化器預設為Newtonsoft.Json,看來微軟現在想得很開,都開始往自己的開發環境裡加入第三方的庫了。但需要知道Newtonsoft.Json則個庫也是在不斷更新的,最好用NuGet來擷取其最新版本。

我個人認為Newtonsoft.Json是相當不錯的,對IEnumerable,IDictionary等介面都支援得很好,下面是個簡單的控制台例子,我們可以用它來觀察JSON的序列化情況:

Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(testObject)); 

只有一行,是否非常簡單?具體Newtonsoft.Json的使用可以參考它的官方網站:http://json.codeplex.com

我這裡特別要說明的一個問題是關於JSON的日期格式問題,眾所周知,使用JSON的用戶端大多是瀏覽器,瀏覽器的Javascript對日期格式的處理能力是要遠遠差於C#/Java之類的,預設情況下,日期會被序列化為“2012-10-12T13:18:20.1656358+08:00”這樣的格式,直接把這個顯示出來明顯不夠友好,假如我光是想顯示“2012-10-12”,是不是就得用Javascript去操作這個字串截取前10個字元?這樣很不優雅並且不能確保一定可行,如果預設格式不是這樣呢?對吧。後來我研究出一種方法,能很好解決這個問題(花了不少時間來搜尋,咳咳……),我們來給Newtonsoft.Json添些料:

namespace Newtonsoft.Json.Converters{    public class SimpleDateConverter : DateTimeConverterBase    {        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)        {            DateTime date = new DateTime();            DateTime.TryParse((string)reader.Value, out date);            return date;        }        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)        {            writer.WriteValue(((DateTime)value).ToString("yyyy-MM-dd"));        }    }}

然後像這樣修飾DateTime類型的屬性:

    [JsonConverter(typeof(SimpleDateConverter))]    public DateTime Dt {get;set;}
六、測試序列化和還原序列化的一致性

序列化和還原序列化必須保持一致,否則程式就會亂套,如何確保?當然是測試一下,本文的開頭提供的那個連結指向的那篇文章,在文章最後就提供了一套很不錯的方法,使用起來很簡單,我這裡就借用下它上面的代碼:

string Serialize<T>(MediaTypeFormatter formatter, T value){    // Create a dummy HTTP Content.    Stream stream = new MemoryStream();    var content = new StreamContent(stream);    /// Serialize the object.    formatter.WriteToStreamAsync(typeof(T), value, stream, content.Headers, null).Wait();    // Read the serialized string.    stream.Position = 0;    return content.ReadAsStringAsync().Result;}T Deserialize<T>(MediaTypeFormatter formatter, string str) where T : class{    // Write the serialized string to a memory stream.    Stream stream = new MemoryStream();    StreamWriter writer = new StreamWriter(stream);    writer.Write(str);    writer.Flush();    stream.Position = 0;    // Deserialize to an object of type T    return formatter.ReadFromStreamAsync(typeof(T), stream, null, null).Result as T;}// Example of usevoid TestSerialization(){    var value = new Person() { Name = "Alice", Age = 23 };    var xml = new XmlMediaTypeFormatter();    string str = Serialize(xml, value);    var json = new JsonMediaTypeFormatter();    str = Serialize(json, value);    // Round trip    Person person2 = Deserialize<Person>(json, str);}

這段代碼寫得很不錯!

七,只返回XML或只返回JSON

如果你有特殊的需要,只允許返回XML或只允許返回JSON的話,(雖然很不建議這樣)那麼可以把對應的formatter拿掉即可。例如你可以這樣拿掉XML Formatter(代碼寫在WebApiConfig.cs中):

    foreach (var fmt in config.Formatters)    {                System.Diagnostics.Debug.WriteLine(fmt.GetType());        if (fmt is System.Net.Http.Formatting.XmlMediaTypeFormatter)        {            config.Formatters.Remove(fmt);            break;        }    }

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.