第三节:框架前期准备篇之利用Newtonsoft.Json改造MVC默认的JsonResult
一. 背景
这里简单的分析一下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,可参考:
2. mvc中的过滤器,可参考:
二. 测试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类型进行扩展,代码如下:
1 /** 2 * 对date的扩展,将 date 转化为指定格式的string 3 * 月(m)、日(d)、12小时(h)、24小时(h)、分(m)、秒(s)、周(e)、季度(q) 可以用 1-2 个占位符 4 * 年(y)可以用 1-4 个占位符,毫秒(s)只能用 1 个占位符(是 1-3 位的数字) 5 * eg: 6 * (new date()).pattern("yyyy-mm-dd hh:mm:ss.s") ==> 2006-07-02 08:09:04.423 7 * (new date()).pattern("yyyy-mm-dd e hh:mm:ss") ==> 2009-03-10 二 20:09:04 8 * (new date()).pattern("yyyy-mm-dd ee hh:mm:ss") ==> 2009-03-10 周二 08:09:04 9 * (new date()).pattern("yyyy-mm-dd eee hh:mm:ss") ==> 2009-03-10 星期二 08:09:04 10 * (new date()).pattern("yyyy-m-d h:m:s.s") ==> 2006-7-2 8:9:4.18 11 12 使用:(eval(value.replace(/\/date\((\d+)\)\//gi, "new date($1)"))).pattern("yyyy-m-d h:m:s.s"); 13 14 */ 15 date.prototype.pattern = function (fmt) { 16 var o = { 17 "m+": this.getmonth() + 1, //月份 18 "d+": this.getdate(), //日 19 "h+": this.gethours() % 12 == 0 ? 12 : this.gethours() % 12, //小时 20 "h+": this.gethours(), //小时 21 "m+": this.getminutes(), //分 22 "s+": this.getseconds(), //秒 23 "q+": math.floor((this.getmonth() + 3) / 3), //季度 24 "s": this.getmilliseconds() //毫秒 25 }; 26 var week = { 27 "0": "/u65e5", 28 "1": "/u4e00", 29 "2": "/u4e8c", 30 "3": "/u4e09", 31 "4": "/u56db", 32 "5": "/u4e94", 33 "6": "/u516d" 34 }; 35 if (/(y+)/.test(fmt)) { 36 fmt = fmt.replace(regexp.$1, (this.getfullyear() + "").substr(4 - regexp.$1.length)); 37 } 38 if (/(e+)/.test(fmt)) { 39 fmt = fmt.replace(regexp.$1, ((regexp.$1.length > 1) ? (regexp.$1.length > 2 ? "/u661f/u671f" : "/u5468") : "") + week[this.getday() + ""]); 40 } 41 for (var k in o) { 42 if (new regexp("(" + k + ")").test(fmt)) { 43 fmt = fmt.replace(regexp.$1, (regexp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 44 } 45 } 46 return fmt; 47 }
在前端这么使用,就可以将时间转换成正常的显示:(详细的见上面的代码)
三. 自我改造
有了前面的jsonresult的代码分析,这里先写一种最简单粗暴的改造方式,当然需要实现安装 newtonsoft.json程序集。
改造方案一:
新建ypfsimplejsonresult类,继承actionresult类,利用构造函数传递数据,override executeresult方法,在里面利用newtonsoft进行改写,代码如下:
1 /// <summary> 2 /// 简洁版的改写,只是替换了实现方式 3 /// </summary> 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方法中进行业务的编写。(这是过滤器执行顺序决定的)
代码分享:
1 public class ypfjsonfilter: actionfilterattribute 2 { 3 4 public override void onactionexecuted(actionexecutedcontext filtercontext) 5 { 6 if (filtercontext.result is jsonresult 7 && !(filtercontext.result is ypfjsonresult)) 8 { 9 jsonresult jsonresult = (jsonresult)filtercontext.result; 10 ypfjsonresult jsonnetresult = new ypfjsonresult(); 11 jsonnetresult.contentencoding = jsonresult.contentencoding; 12 jsonnetresult.contenttype = jsonresult.contenttype; 13 jsonnetresult.data = jsonresult.data; 14 jsonnetresult.jsonrequestbehavior = jsonresult.jsonrequestbehavior; 15 jsonnetresult.maxjsonlength = jsonresult.maxjsonlength; 16 jsonnetresult.recursionlimit = jsonresult.recursionlimit; 17 filtercontext.result = jsonnetresult; 18 } 19 } 20 21 22 }
编写完过滤器后,需要全局注册一下:
可以在在filterconfig文件中注册 filters.add(new ypfjsonfilter());
或者直接去:global文件中:globalfilters.filters.add(new ypfjsonfilter()); 代码来注册,道理都一样
接口代码,不需要做任何改变,继续沿用return json()即可。
测试结果:
!
- 作 者 : yaopengfei(姚鹏飞)
- 博客地址 :
- 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
上一篇: 3799元起!荣耀智慧屏售价公布:两款55英寸、8月15日首销
下一篇: 吃饱穿暖