第三节:框架前期准备篇之利用Newtonsoft.Json改造MVC默认的JsonResult

一. 背景   在MVC框架中,我们可能经常会用到 return Json(),而Json方法内部又是一个JsonResult类,那么JsonResult内部又是什么原理呢?在MVC框架中,各种xxxResult便捷了我们的开发,但这些都不是本节的重点,在这里我们只需要知道JsonResult内部的原理即可。   JsonResult内部原理是基于 JavaScriptSerializer来做的序列化,在使用过程中,有这么几个弊端:   ①:DateTime类型返回给前端是这个玩意:\/Date(1535009968228)\/ ,相当别扭。(PS:前端有很多办法处理的)   ②:对于前端而言,对于属性名可能更倾向于小写开头,但在C#中,很多都是大写,但JsonResult将原结果默认返回给前端,前端人员可能会有点小不爽。(PS:这也可以算作是一个习惯问题,没有明确的对错)   ③:循环引用的问题。  关于使用Newtonsoft.Json改造MVC默认的JsonResult,有很多种方式,本节仅是整理了一下在我日常开发中的使用方法。(PS:这里的MVC版本为: 5.2.4.0)   这里简单的分析一下JsonResult的源码: ①:继承了ActionResult, 实现了ExecuteResult方法。 ②:解读源码可知,JsonResult内部实现原理是调用了JavaScriptSerializer对象中的Serialize方法,将Json对象转换成了Json字符串,通过:response.Write(javaScriptSerializer.Serialize(this.Data)); 传递给前台。 ③:默认是禁止Get请求访问的. JsonRequestBehavior.DenyGet。 ④:在MVC的Action中,return Json(),这里的Json通过源码可知,即new了一个JsonResult对象而已,并且MVC中封装了很多重载。   本节涉及到的知识点有:     1. MVC中的各种Result,可参考:http://www.cnblogs.com/yaopengfei/p/7910767.html     2. MVC中的过滤器,可参考:https://www.cnblogs.com/yaopengfei/p/7910763.html 二. 测试JsonResult的弊端   这里主要测试一下DateTime类型“乱码”问题和默认大小写问题。 后台代码: 复制代码 1 public ActionResult Json1() 2 { 3 var msg = new 4 { 5 ID = 1, 6 Name = "ypf1", 7 time = DateTime.Now 8 }; 9 return Json(msg); 10 } 复制代码 前台代码: 复制代码 1 $("#btn1").on("click", function () { 2 $.post("Json1", {}, function (data) { 3 console.log(data); 4 }); 5 }); 复制代码 测试结果: 下面提供一种解决时间乱码的问题,使用该js文件,对Date类型进行扩展,代码如下: View Code 在前端这么使用,就可以将时间转换成正常的显示:(详细的见上面的代码) 三. 自我改造   有了前面的JsonResult的代码分析,这里先写一种最简单粗暴的改造方式,当然需要实现安装 Newtonsoft.Json程序集。 改造方案一:   新建YpfSimpleJsonResult类,继承ActionResult类,利用构造函数传递数据,override ExecuteResult方法,在里面利用Newtonsoft进行改写,代码如下: 复制代码 1 /// 2 /// 简洁版的改写,只是替换了实现方式 3 /// 4 public class YpfSimpleJsonResult : ActionResult 5 { 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 } 复制代码 测试接口: 复制代码 1 public ActionResult Json3() 2 { 3 var msg = new 4 { 5 ID = 1, 6 Name = "ypf1", 7 time = DateTime.Now 8 }; 9 return new YpfSimpleJsonResult(msg); 10 } 复制代码 测试结果: 改造方案二:   有了上面的方案的基础,下面深度改造一下,新建YpfJsonResult类,直接继承高层封装JsonResult类,并配置引用问题、默认小写问题、自定义时间格式,代码如下: 复制代码 1 public class YpfJsonResult : JsonResult 2 { 3 public YpfJsonResult() 4 { 5 Settings = new JsonSerializerSettings 6 { 7 //1. 忽略循环引用问题,建议设置为Error,这样的话遇到循环引用的时候报错 8 ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 9 //2. 日期格式化,这里可以将Newtonsoft默认的格式进行修改 10 DateFormatString = "yyyy-MM-dd HH:mm:ss", 11 //3. 设置属性为开头字母小写的驼峰命名 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 } 复制代码 测试接口: 复制代码 1 public ActionResult Json2() 2 { 3 var msg = new 4 { 5 ID = 1, 6 Name = "ypf1", 7 time = DateTime.Now 8 }; 9 //注意:这里的Data是JsonResult类中的一个获取和设置数据的属性。 10 return new YpfJsonResult() { Data = msg }; 11 } 复制代码 测试结果: 总结:   虽然我们通过第二套方案已经达到了我们的目的,但它存在一个弊端,就是侵入性太强,每个方法中都要改写,那么有没有一种方式可以全局控制呢?   显然是有的,可以考虑使用全局过滤器。 四. 全局处理   这里换一种思路,通过注册一个全局过滤器,对每个Action进行监测,如果使用的是JsonResult,就把JsonResult替换成自己编写的YpfJsonResult,这样的话业务中的调用代码,不需要发生任何变化,仍然可以使用 return Json()方法。   特别注意:这里的过滤器要使用行为过滤器,并且要在OnActionExecuted方法中进行业务的编写。(这是过滤器执行顺序决定的) 代码分享: 过滤器代码 编写完过滤器后,需要全局注册一下:   可以在在FilterConfig文件中注册 filters.Add(new YpfJsonFilter());   或者直接去:Global文件中:GlobalFilters.Filters.Add(new YpfJsonFilter()); 代码来注册,道理都一样 接口代码,不需要做任何改变,继续沿用return Json()即可。 测试结果: ! 作 者 : Yaopengfei(姚鹏飞) 博客地址 : http://www.cnblogs.com/yaopengfei/ 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。https://www.cnblogs.com/yaopengfei/p/9518725.html
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信