欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-05-13 22:53:35
一. 背景 在MVC框架中,我们可能经常会用到 return Json(),而Json方法内部又是一个JsonResult类,那么JsonResult内部又是什么原理呢?在MVC框架中,各种xxxResult便捷了我们的开发,但这些都不是本节的重点,在这里我们只需要知道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中封装了很多重载。

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

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

  本节涉及到的知识点有:

    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    });

测试结果:

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

 

下面提供一种解决时间戳转换的问题,使用该js文件,对date类型进行扩展,代码如下:

第三节:框架前期准备篇之利用Newtonsoft.Json改造MVC默认的JsonResult
 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 }
view code

在前端这么使用,就可以将时间转换成正常的显示:(详细的见上面的代码)

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

三. 自我改造

   有了前面的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         }

测试结果:

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

 

改造方案二:

  有了上面的方案的基础,下面深度改造一下,新建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    }

测试结果:

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

总结:

   虽然我们通过第二套方案已经达到了我们的目的,但它存在一个弊端,就是侵入性太强,每个方法中都要改写,那么有没有一种方式可以全局控制呢?

  显然是有的,可以考虑使用全局过滤器。

 

四. 全局处理

   这里换一种思路,通过注册一个全局过滤器,对每个action进行监测,如果使用的是jsonresult,就把jsonresult替换成自己编写的ypfjsonresult,这样的话业务中的调用代码,不需要发生任何变化,仍然可以使用 return json()方法。

  特别注意:这里的过滤器要使用行为过滤器,并且要在onactionexecuted方法中进行业务的编写。(这是过滤器执行顺序决定的)

代码分享:

第三节:框架前期准备篇之利用Newtonsoft.Json改造MVC默认的JsonResult
 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()即可。

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

测试结果:

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

 

 

 

 

 

!

  • 作       者 : yaopengfei(姚鹏飞)
  • 博客地址 :
  • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。