第三节:框架前期准备篇之利用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,可参考:http://www.cnblogs.com/yaopengfei/p/7910767.html
2. MVC中的过滤器,可参考:https://www.cnblogs.com/yaopengfei/p/7910763.html
二. 测试JsonResult的弊端
这里主要测试一下DateTime类型“乱码”(时间戳)问题和默认大小写问题。
后台代码:
public ActionResult Json1()
{
var msg = new
{
ID = ,
Name = "ypf1",
time = DateTime.Now
};
return Json(msg);
}
前台代码:
$("#btn1").on("click", function () {
$.post("Json1", {}, function (data) {
console.log(data);
});
});
测试结果:

下面提供一种解决时间戳转换的问题,使用该js文件,对Date类型进行扩展,代码如下:
/**
* 对Date的扩展,将 Date 转化为指定格式的String
* 月(M)、日(d)、12小时(h)、24小时(H)、分(m)、秒(s)、周(E)、季度(q) 可以用 1-2 个占位符
* 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
* eg:
* (new Date()).pattern("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
* (new Date()).pattern("yyyy-MM-dd E HH:mm:ss") ==> 2009-03-10 二 20:09:04
* (new Date()).pattern("yyyy-MM-dd EE hh:mm:ss") ==> 2009-03-10 周二 08:09:04
* (new Date()).pattern("yyyy-MM-dd EEE hh:mm:ss") ==> 2009-03-10 星期二 08:09:04
* (new Date()).pattern("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 使用:(eval(value.replace(/\/Date\((\d+)\)\//gi, "new Date($1)"))).pattern("yyyy-M-d h:m:s.S"); */
Date.prototype.pattern = function (fmt) {
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours() % 12 == 0 ? 12 : this.getHours() % 12, //小时
"H+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
var week = {
"0": "/u65e5",
"1": "/u4e00",
"2": "/u4e8c",
"3": "/u4e09",
"4": "/u56db",
"5": "/u4e94",
"6": "/u516d"
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
}
if (/(E+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? "/u661f/u671f" : "/u5468") : "") + week[this.getDay() + ""]);
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
}
}
return fmt;
}
在前端这么使用,就可以将时间转换成正常的显示:(详细的见上面的代码)

三. 自我改造
有了前面的JsonResult的代码分析,这里先写一种最简单粗暴的改造方式,当然需要实现安装 Newtonsoft.Json程序集。
改造方案一:
新建YpfSimpleJsonResult类,继承ActionResult类,利用构造函数传递数据,override ExecuteResult方法,在里面利用Newtonsoft进行改写,代码如下:
/// <summary>
/// 简洁版的改写,只是替换了实现方式
/// </summary>
public class YpfSimpleJsonResult : ActionResult
{
private object _Data = null;
public YpfSimpleJsonResult(object data)
{
this._Data = data;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.ContentType = "application/json";
context.HttpContext.Response.Write(JsonConvert.SerializeObject(this._Data));
}
}
测试接口:
public ActionResult Json3()
{
var msg = new
{
ID = ,
Name = "ypf1",
time = DateTime.Now
};
return new YpfSimpleJsonResult(msg);
}
测试结果:

改造方案二:
有了上面的方案的基础,下面深度改造一下,新建YpfJsonResult类,直接继承高层封装JsonResult类,并配置引用问题、默认小写问题、自定义时间格式,代码如下:
public class YpfJsonResult : JsonResult
{
public YpfJsonResult()
{
Settings = new JsonSerializerSettings
{
//1. 忽略循环引用问题,建议设置为Error,这样的话遇到循环引用的时候报错
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
//2. 日期格式化,这里可以将Newtonsoft默认的格式进行修改
DateFormatString = "yyyy-MM-dd HH:mm:ss",
//3. 设置属性为开头字母小写的驼峰命名
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
} public JsonSerializerSettings Settings { get; private set; } public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("GET is not allowed");
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType;
if (this.ContentEncoding != null)
{
response.ContentEncoding = this.ContentEncoding;
}
if (this.Data == null)
{
return;
}
var scriptSerializer = JsonSerializer.Create(this.Settings);
scriptSerializer.Serialize(response.Output, this.Data);
}
}
测试接口:
public ActionResult Json2()
{
var msg = new
{
ID = ,
Name = "ypf1",
time = DateTime.Now
};
//注意:这里的Data是JsonResult类中的一个获取和设置数据的属性。
return new YpfJsonResult() { Data = msg };
}
测试结果:

总结:
虽然我们通过第二套方案已经达到了我们的目的,但它存在一个弊端,就是侵入性太强,每个方法中都要改写,那么有没有一种方式可以全局控制呢?
显然是有的,可以考虑使用全局过滤器。
四. 全局处理
这里换一种思路,通过注册一个全局过滤器,对每个Action进行监测,如果使用的是JsonResult,就把JsonResult替换成自己编写的YpfJsonResult,这样的话业务中的调用代码,不需要发生任何变化,仍然可以使用 return Json()方法。
特别注意:这里的过滤器要使用行为过滤器,并且要在OnActionExecuted方法中进行业务的编写。(这是过滤器执行顺序决定的)
代码分享:
public class YpfJsonFilter: ActionFilterAttribute
{ public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Result is JsonResult
&& !(filterContext.Result is YpfJsonResult))
{
JsonResult jsonResult = (JsonResult)filterContext.Result;
YpfJsonResult jsonNetResult = new YpfJsonResult();
jsonNetResult.ContentEncoding = jsonResult.ContentEncoding;
jsonNetResult.ContentType = jsonResult.ContentType;
jsonNetResult.Data = jsonResult.Data;
jsonNetResult.JsonRequestBehavior = jsonResult.JsonRequestBehavior;
jsonNetResult.MaxJsonLength = jsonResult.MaxJsonLength;
jsonNetResult.RecursionLimit = jsonResult.RecursionLimit;
filterContext.Result = jsonNetResult;
}
} }
过滤器代码
编写完过滤器后,需要全局注册一下:
可以在在FilterConfig文件中注册 filters.Add(new YpfJsonFilter());
或者直接去:Global文件中:GlobalFilters.Filters.Add(new YpfJsonFilter()); 代码来注册,道理都一样
接口代码,不需要做任何改变,继续沿用return Json()即可。

测试结果:

!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
第三节:框架前期准备篇之利用Newtonsoft.Json改造MVC默认的JsonResult的更多相关文章
- 框架前期准备篇之AutoFac常见用法总结 转载
框架前期准备篇之AutoFac常见用法总结 一. 说在前面的话 凡是大约工作在两年以上的朋友们,或多或少都会接触到一些框架搭建方面的知识,只要一谈到框架搭建这个问题或者最佳用法这个问题,势必会引起一点 ...
- asp.net MVC 框架中控制器里使用Newtonsoft.Json对前端传过来的字符串进行解析
下面我用一个实例来和大家分享一下我的经验,asp.net MVC 框架中控制器里使用Newtonsoft.Json对前端传过来的字符串进行解析. using Newtonsoft.Json; usin ...
- 利用Newtonsoft.Json实现Json序列化与反序列化
在项目中用到了Newtonsoft.Json来实现序列化和反序列化,在这里写下实现代码. 1.创建类用于排除不序列化的属性 public class ExcludePropertiesContract ...
- C# 利用Newtonsoft.Json进行Json序列化与反序列化
我们可以通过DataContractJsonSerializer类来序列化一个对象为json字符串. public class JsonConvert<T> { public static ...
- C#利用newtonsoft.json读取.so配置文件内容
今天花 了点时间来使用 C#读取json文件 ,文件后缀为 .so文件 ,也是基于文件流的形式 获取 对象 ,然后解析; 之所以尝试 使用 json读取 ,是因为其配置文件的格式 更为友好 和方便,直 ...
- C# 利用Newtonsoft.Json 序列化生成Json数据
现在需要将一些数据转化成json格式返回给调用者, 使用Newtonsoft.Json.DLL库来帮助我们序列化 举例: {"300033":{"MC":&qu ...
- 第一节:框架前期准备篇之Log4Net日志详解
一. Log4Net简介 Log4net是从Java中的Log4j迁移过来的一个.Net版的开源日志框架,它的功能很强大,可以将日志分为不同的等级,以不同的格式输出到不同的存储介质中,比如:数据库.t ...
- 第二节:框架前期准备篇之AutoFac常见用法总结
一. 说在前面的话 凡是大约工作在两年以上的朋友们,或多或少都会接触到一些框架搭建方面的知识,只要一谈到框架搭建这个问题或者最佳用法这个问题,势必会引起一点点小小的风波,我说我的好,他说他的好,非常容 ...
- 第四节:框架前期准备篇之进程外Session的两种配置方式
一. 基本介绍 1. 背景:Asp.Net默认的Session机制是进程内,存储在服务器端内存中,有这么几个缺点: ①:既然存在内存中,空间有限,不能存储大数据量信息,数据量多的话Session会被挤 ...
随机推荐
- 使用mybatis报错【Result Maps collection already contains value for ...BaseResultMap】的解决方法
Result Maps collection already contains value for ...BaseResultMap ...... 这个问题,相信大家在使用mybatis的重新生成 d ...
- docker容器日志收集方案(方案二 filebeat+syslog本地日志收集)
与方案一一样都是把日志输出到本地文件系统使用filebeat进行扫描采集 不同的是输出的位置是不一样的 我们对docker进行如下设置 sudo docker service update --lo ...
- spark-RDD源码分析
http://stark-summer.iteye.com/blog/2178096 RDD的核心方法: 首先看一下getPartitions方法的源码: getPartitions返回的是一系列pa ...
- Spark的性能调优杂谈
下面这些关于Spark的性能调优项,有的是来自官方的,有的是来自别的的工程师,有的则是我自己总结的. 基本概念和原则 <1> 每一台host上面可以并行N个worker,每一个worke ...
- CentOS 7 中使用NTP进行时间同步
1. NTP时钟同步方式说明NTP在linux下有两种时钟同步方式,分别为直接同步和平滑同步: 直接同步 使用ntpdate命令进行同步,直接进行时间变更.如果服务器上存在一个12点运行的任务,当前服 ...
- cumprod、prod函数
1.prod函数 prod函数用于求矩阵元素的积,其调用格式如下. (1)B=prod(A):若A为向量,则返回所有元素的积:若A为矩阵,则返回各列所有元素的积. (2)B=prod(A,dim):返 ...
- Linux内存管理 (4)分配物理页面
专题:Linux内存管理专题 关键词:分配掩码.伙伴系统.水位(watermark).空闲伙伴块合并. 我们知道Linux内存管理是以页为单位进行的,对内存的管理是通过伙伴系统进行. 从Linux内存 ...
- spring @CrossOrigin解决跨域问题
阅读目录: 一.跨域(CORS)支持: 二.使用方法: 1.controller配置CORS 2.全局CORS配置 3.XML命名空间 4.How does it work? 5.基于过滤器的CORS ...
- SQL Server之深入理解STUFF
前言 最近项目无论查询报表还是其他数据都在和SQL Server数据库打交道,对于STUFF也有了解,但是发现当下一次再写SQL语句时我还得查看相关具体用法,说到底还是没有完全理解其原理,所以本节我们 ...
- JS 设计模式二 -- 单例模式
单例模式 概念 单例模式 就是保证一个类只有一个实例,并提供一个访问它的全局访问点. 实现方法 先判断实例是否存在,如果存在直接返回,如果不存在就创建实例后在返回,确保了一个类只有一个实例对象. va ...