第三节:框架前期准备篇之利用Newtonsoft.Json改造MVC默认的JsonResult
一. 背景
在MVC框架中,我們可能經(jīng)常會(huì)用到 return Json(),而Json方法內(nèi)部又是一個(gè)JsonResult類,那么JsonResult內(nèi)部又是什么原理呢?在MVC框架中,各種xxxResult便捷了我們的開(kāi)發(fā),但這些都不是本節(jié)的重點(diǎn),在這里我們只需要知道JsonResult內(nèi)部的原理即可。
JsonResult內(nèi)部原理是基于 JavaScriptSerializer來(lái)做的序列化,在使用過(guò)程中,有這么幾個(gè)弊端:
①:DateTime類型返回給前端格式不友好:\/Date(1535009968228)\/ ,相當(dāng)別扭。(PS:前端有很多辦法處理的)
②:對(duì)于前端而言,對(duì)于屬性名可能更傾向于小寫開(kāi)頭,但在C#中,很多都是大寫,但JsonResult將原結(jié)果默認(rèn)返回給前端,前端人員可能會(huì)有點(diǎn)小不爽。(PS:這也可以算作是一個(gè)習(xí)慣問(wèn)題,沒(méi)有明確的對(duì)錯(cuò))
③:循環(huán)引用的問(wèn)題。
? 關(guān)于使用Newtonsoft.Json改造MVC默認(rèn)的JsonResult,有很多種方式,本節(jié)僅是整理了一下在我日常開(kāi)發(fā)中的使用方法。(PS:這里的MVC版本為:?5.2.4.0)
? 這里簡(jiǎn)單的分析一下JsonResult的源碼:
?①:繼承了ActionResult, 實(shí)現(xiàn)了ExecuteResult方法。
?②:解讀源碼可知,JsonResult內(nèi)部實(shí)現(xiàn)原理是調(diào)用了JavaScriptSerializer對(duì)象中的Serialize方法,將Json對(duì)象轉(zhuǎn)換成了Json字符串,通過(guò):response.Write(javaScriptSerializer.Serialize(this.Data)); 傳遞給前臺(tái)。
?③:默認(rèn)是禁止Get請(qǐng)求訪問(wèn)的. JsonRequestBehavior.DenyGet。
?④:在MVC的Action中,return Json(),這里的Json通過(guò)源碼可知,即new了一個(gè)JsonResult對(duì)象而已,并且MVC中封裝了很多重載。
?
本節(jié)涉及到的知識(shí)點(diǎn)有:
1. MVC中的各種Result,可參考:http://www.cnblogs.com/yaopengfei/p/7910767.html
2. MVC中的過(guò)濾器,可參考:https://www.cnblogs.com/yaopengfei/p/7910763.html
二. 測(cè)試JsonResult的弊端
? 這里主要測(cè)試一下DateTime類型“亂碼”(時(shí)間戳)問(wèn)題和默認(rèn)大小寫問(wèn)題。
后臺(tái)代碼:
1 public ActionResult Json1()2 {3 var msg = new4 {5 ID = 1,6 Name = "ypf1",7 time = DateTime.Now8 };9 return Json(msg); 10 }前臺(tái)代碼:
1 $("#btn1").on("click", function () { 2 $.post("Json1", {}, function (data) { 3 console.log(data); 4 }); 5 });測(cè)試結(jié)果:
?
下面提供一種解決時(shí)間戳轉(zhuǎn)換的問(wèn)題,使用該js文件,對(duì)Date類型進(jìn)行擴(kuò)展,代碼如下:
?View Code
在前端這么使用,就可以將時(shí)間轉(zhuǎn)換成正常的顯示:(詳細(xì)的見(jiàn)上面的代碼)
?
三. 自我改造
? 有了前面的JsonResult的代碼分析,這里先寫一種最簡(jiǎn)單粗暴的改造方式,當(dāng)然需要實(shí)現(xiàn)安裝 Newtonsoft.Json程序集。
改造方案一:
? 新建YpfSimpleJsonResult類,繼承ActionResult類,利用構(gòu)造函數(shù)傳遞數(shù)據(jù),override ExecuteResult方法,在里面利用Newtonsoft進(jìn)行改寫,代碼如下:
1 /// <summary>2 /// 簡(jiǎn)潔版的改寫,只是替換了實(shí)現(xiàn)方式3 /// </summary>4 public class YpfSimpleJsonResult : ActionResult5 {6 private object _Data = null;7 public YpfSimpleJsonResult(object data)8 {9 this._Data = data; 10 } 11 public override void ExecuteResult(ControllerContext context) 12 { 13 context.HttpContext.Response.ContentType = "application/json"; 14 context.HttpContext.Response.Write(JsonConvert.SerializeObject(this._Data)); 15 } 16 }測(cè)試接口:
1 public ActionResult Json3()2 {3 var msg = new4 {5 ID = 1,6 Name = "ypf1",7 time = DateTime.Now8 };9 return new YpfSimpleJsonResult(msg); 10 }測(cè)試結(jié)果:
?
改造方案二:
有了上面的方案的基礎(chǔ),下面深度改造一下,新建YpfJsonResult類,直接繼承高層封裝JsonResult類,并配置引用問(wèn)題、默認(rèn)小寫問(wèn)題、自定義時(shí)間格式,代碼如下:
1 public class YpfJsonResult : JsonResult2 {3 public YpfJsonResult()4 {5 Settings = new JsonSerializerSettings6 {7 //1. 忽略循環(huán)引用問(wèn)題,建議設(shè)置為Error,這樣的話遇到循環(huán)引用的時(shí)候報(bào)錯(cuò)8 ReferenceLoopHandling = ReferenceLoopHandling.Ignore,9 //2. 日期格式化,這里可以將Newtonsoft默認(rèn)的格式進(jìn)行修改 10 DateFormatString = "yyyy-MM-dd HH:mm:ss", 11 //3. 設(shè)置屬性為開(kāi)頭字母小寫的駝峰命名 12 ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() 13 }; 14 } 15 16 public JsonSerializerSettings Settings { get; private set; } 17 18 public override void ExecuteResult(ControllerContext context) 19 { 20 if (context == null) 21 { 22 throw new ArgumentNullException("context"); 23 } 24 if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) 25 { 26 throw new InvalidOperationException("GET is not allowed"); 27 } 28 HttpResponseBase response = context.HttpContext.Response; 29 response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType; 30 if (this.ContentEncoding != null) 31 { 32 response.ContentEncoding = this.ContentEncoding; 33 } 34 if (this.Data == null) 35 { 36 return; 37 } 38 var scriptSerializer = JsonSerializer.Create(this.Settings); 39 scriptSerializer.Serialize(response.Output, this.Data); 40 } 41 }測(cè)試接口:
1 public ActionResult Json2()2 {3 var msg = new4 {5 ID = 1,6 Name = "ypf1",7 time = DateTime.Now8 };9 //注意:這里的Data是JsonResult類中的一個(gè)獲取和設(shè)置數(shù)據(jù)的屬性。 10 return new YpfJsonResult() { Data = msg }; 11 }測(cè)試結(jié)果:
總結(jié):
? 雖然我們通過(guò)第二套方案已經(jīng)達(dá)到了我們的目的,但它存在一個(gè)弊端,就是侵入性太強(qiáng),每個(gè)方法中都要改寫,那么有沒(méi)有一種方式可以全局控制呢?
顯然是有的,可以考慮使用全局過(guò)濾器。
?
四. 全局處理
? 這里換一種思路,通過(guò)注冊(cè)一個(gè)全局過(guò)濾器,對(duì)每個(gè)Action進(jìn)行監(jiān)測(cè),如果使用的是JsonResult,就把JsonResult替換成自己編寫的YpfJsonResult,這樣的話業(yè)務(wù)中的調(diào)用代碼,不需要發(fā)生任何變化,仍然可以使用 return Json()方法。
特別注意:這里的過(guò)濾器要使用行為過(guò)濾器,并且要在OnActionExecuted方法中進(jìn)行業(yè)務(wù)的編寫。(這是過(guò)濾器執(zhí)行順序決定的)
代碼分享:
?過(guò)濾器代碼
編寫完過(guò)濾器后,需要全局注冊(cè)一下:
可以在在FilterConfig文件中注冊(cè) filters.Add(new YpfJsonFilter());
或者直接去:Global文件中:GlobalFilters.Filters.Add(new YpfJsonFilter()); 代碼來(lái)注冊(cè),道理都一樣
接口代碼,不需要做任何改變,繼續(xù)沿用return Json()即可。
測(cè)試結(jié)果:
?
?
?
?
?
?
!
- 作???????者 :?Yaopengfei(姚鵬飛)
- 博客地址 :?http://www.cnblogs.com/yaopengfei/
- 聲?????明1 : 本人才疏學(xué)淺,用郭德綱的話說(shuō)“我是一個(gè)小學(xué)生”,如有錯(cuò)誤,歡迎討論,請(qǐng)勿謾罵^_^。
- 聲?????明2 : 原創(chuàng)博客請(qǐng)?jiān)谵D(zhuǎn)載時(shí)保留原文鏈接或在文章開(kāi)頭加上本人博客地址,否則保留追究法律責(zé)任的權(quán)利。
總結(jié)
以上是生活随笔為你收集整理的第三节:框架前期准备篇之利用Newtonsoft.Json改造MVC默认的JsonResult的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: x86 CPU 危!最新漏洞引发热议 黑
- 下一篇: 三层架构与MVC的区别