了解ASP.NET MVC几种ActionResult的本质:JavaScriptResult & JsonResult
在之前的两篇文章(《EmptyResult & ContentResult》和《FileResult》)我们剖析了EmptyResult、ContentResult和FileResult这三种ActionResult是如何将Action执行的结果响应给客户端的。本篇文章着重介绍在进行Ajax调用中经常使用的两个ActionResult,即JavaScriptResult和JsonResult。[本文已经同步到《How ASP.NET MVC Works?》中]
目录
一、JavaScriptResult
二、实例演示:通过JavaScriptResult返回字段在客户端自动执行的JavaScript
三、JsonResult
一、JavaScriptResult
JavaScriptResult使我们可以在服务端动态地生成一段JavaScript脚本,并以此作为请求的响应,而这段脚本会在客户端被执行。其实JavaScriptResult的实现非常简单,它仅仅是将表示JavaScript脚本的字符串通过当前的HttpResponse响应给请求的客户端而已。如下面的代码片断所示,JavaScriptResult的属性Script表示响应的JavaScript脚本,而用于响应JavaScript脚本的ExecuteResult方法除了将脚本内容写入当前HttpResponse之外,还会将响应的媒体类型设置为“application/x-javascript”(不是“text/javascript”)。
1: public class JavaScriptResult : ActionResult
2: {
3: public override void ExecuteResult(ControllerContext context)
4: {
5: HttpResponseBase response = context.HttpContext.Response;
6: response.ContentType = "application/x-javascript";
7: response.Write(this.Script);
8: }
9: public string Script { get; set; }
10: }
11:
12: public abstract class Controller : ControllerBase, ...
13: {
14: //其他成员
15: protected virtual JavaScriptResult JavaScript(string script);
16: }
抽象类Controller中定义了如上一个JavaScript方法根据指定的脚本字符串创建一个JavaScriptResult。实际上我们完全可以通过ContentResult来实现与JavaScriptResult一样的脚本响应功能,下面的两段程序是等效的。大部分浏览器会将媒体类型“application/x-javascript”等同于“text/javascript”,所以在通过ContentResult进行脚本响应时将媒体类型设置为“text/javascript”可以起到相同的效果。返回类型为JavaScriptResult的Action方法一般用于处理Ajax请求。
1: //JavaScriptResult:
2: public class FooController : Controller
3: {
4: public ActionResult JavaScript()
5: {
6: return JavaScript("alert('Hello World!');");
7: }
8: }
9:
10: //ContentResult:
11: public class FooController : Controller
12: {
13: public ActionResult JavaScript()
14: {
15: return Content("alert('Hello World!');", "application/x-javascript");
16: }
17: }
二、实例演示:通过JavaScriptResult返回字段在客户端自动执行的JavaScript
我们照例演示一个通过JavaScriptResult进行脚本响应的例子。我们演示一个在线购物的场景:用于完成了商品选购之后提交订单,服务端在处理订单的时候需要确认订购的商品是否超出了对应的库存量,如果存量充裕则正常处理该订单,否则提示库存不足,并将商品实时库存量显示给用户让他修正相应商品的购买量。我们利用JavaScript的方式来提示订单处理结果的消息(成功处理或者库存不足),很显然这段JavaScript应该是动态的(库存量是动态的)。
在通过Visual Studio的ASP.NET MVC项目模板创建的空Web应用中定义一个ShoppingCart类表示购物车。如下面的代码片断所示,ShoppingCart是表示购物车商品项ShoppingCartItem对象的列表,而ShoppingCartItem的三个属性(Id、Name和Quantity)分别表示商品ID、名称和订购数量。
1: public class ShoppingCart : List<ShoppingCartItem>
2: {}
3:
4: public class ShoppingCartItem
5: {
6: public string Id { get; set; }
7: public string Name { get; set; }
8: public int Quantity { get; set; }
9: }
然后我们创建如下一个HomeController。我们在默认的Action方法Index中创建一个包含三个商品的ShoppingCart对象,并将其作为Model呈现在对应的View中。Action方法ProcessOrder用于处理提交的购买订单,如果订购商品的数量没有超过库存量(通过一个静态字典字段stock表示),则通过调用alert函数提示“购物订单成功处理”,否则提示“库存不足”,并将相应商品当前库存量显示出来。
1: public class HomeController : Controller
2: {
3: private static Dictionary<string, int> stock = new Dictionary<string, int>();
4: static HomeController()
5: {
6: stock.Add("001", 20);
7: stock.Add("002", 30);
8: stock.Add("003", 40);
9: }
10: public ActionResult Index()
11: {
12: ShoppingCart cart = new ShoppingCart();
13: cart.Add(new ShoppingCartItem { Id = "001", Quantity=1, Name = "商品A" });
14: cart.Add(new ShoppingCartItem { Id = "002", Quantity = 1, Name = "商品B" });
15: cart.Add(new ShoppingCartItem { Id = "003", Quantity = 1, Name = "商品C" });
16: return View(cart);
17: }
18:
19: public ActionResult ProcessOrder(ShoppingCart cart)
20: {
21: StringBuilder sb = new StringBuilder();
22: foreach (var cartItem in cart)
23: {
24: if (!CheckStock(cartItem.Id, cartItem.Quantity))
25: {
26: sb.Append(string.Format("{0}: {1};", cartItem.Name,stock[cartItem.Id]));
27: }
28: }
29: if(string.IsNullOrEmpty(sb.ToString()))
30: {
31: return Content("alert('购物订单成功处理!');", "text/javascript");
32: }
33: string script = string.Format("alert('库存不足! ({0})');", sb.ToString().TrimEnd(';'));
34: return JavaScript(script);
35: }
36:
37: private bool CheckStock(string id, int quantity)
38: {
39: return stock[id] >= quantity;
40: }
41: }
如下所示的是Action方法Index对应的View的定义,这是一个Model类型为ShoppingCart的强类型View。在一个以Ajax请求提交的表单(表单的Action属性对应着上面定义的Action方法ProcessOrder)中显示了购物车中的商品和数量,用于可以修改订购数量并通过点击“提交订单”按钮以Ajax请求的方式提交订单。
1: @model ShoppingCart
2: <html>
3: <head>
4: <title>用户登录</title>
5: <script type="text/javascript" src="@Url.Content("~/Scripts/jquery-1.6.2.js")"></script>
1: <script type="text/javascript" src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")">
</script>
6: </head>
7: <body>
8: @using (Ajax.BeginForm("ProcessOrder", new AjaxOptions()))
9: {
10: for (int i = 0; i < Model.Count; i++)
11: {
12: <div>
13: @Html.HiddenFor(m=>m[i].Id)
14: @Html.HiddenFor(m => m[i].Name)
15:
16: @Html.DisplayFor(m => m[i].Name):
17: @Html.EditorFor(m => m[i].Quantity)
18: </div>
19: }
20: <input type="submit" value="提交订单" />
21: }
22: </body>
23: </html>
运行我们的程序后,一个包含三个商品的购物车信息会被呈现出来,当我们输入相应的订购数量并点击“提交订单”后,订单处理结果消息会弹出来。下图所示的就是库存不足的情况下显示的消息。
三、JsonResult
JavaScript已经在Web应用中得到广泛的应用,而JSON则成了标准的数据格式。但是对于后台程序来说,数据却是通过一个基于某种CLR类型的对象来承载,当客户端调用某个Action方法并希望以JSON的格式返回请求的数据时,ASP.NET MVC需要有一种机制将CLR对象转换成JSON格式予以响应,而这可以通过JsonResult来解决。如下面的代码片断所示,JsonResult具有一个object类型的属性Data表示需要被转换成JSON格式的数据对象。属性ContentEncoding和ContentType表示为当前响应设置的编码方式和媒体类型,默认采用的媒体类型为“application/json”。
1: public class JsonResult : ActionResult
2: {
3: public override void ExecuteResult(ControllerContext context);
4:
5: public object Data { get; set; }
6: public Encoding ContentEncoding { get; set; }
7: public string ContentType { get; set; }
8: public JsonRequestBehavior JsonRequestBehavior { get; set; }
9: public int? MaxJsonLength { get; set; }
10: public int? RecursionLimit { get; set; }
11: }
12:
13: public enum JsonRequestBehavior
14: {
15: AllowGet,
16: DenyGet
17: }
出于对安全的考虑,JsonResult在默认的情况下不能作为对HTTP-GET请求的响应,在这种情况下并会直接抛出一个InvalidOperationException异常。我们可以通过它的JsonRequestBehavior属性开启JsonResult对HTTP-GET请求的支持。该属性类型为JsonRequestBehavior枚举,两个枚举项AllowGet和DenyGet分别表示允许/拒绝支持对HTTP-GET请求的响应。JsonResult的JsonRequestBehavior属性在初始化的时候被设置为DenyGet,如果我们需要用创建的JsonResult来响应HTTP-GET请求,需要显式地将它的JsonRequestBehavior属性设置为AllowGet。
CLR对象到JSON格式字符串的序列化过程通过具有如下定义的序列化器JavaScriptSerializer来完成。JavaScriptSerializer的Serialize和Deserialize方法实现了CLR对象的序列化和对JSON字符串的反序列化。
1: public class JavaScriptSerializer
2: {
3: //其他成员
4: public string Serialize(object obj);
5: public object Deserialize(string input, Type targetType);
6:
7: public int MaxJsonLength { get; set; }
8: public int RecursionLimit { get; set; }
9: }
JavaScriptSerializer具有两个整型的属性MaxJsonLength和RecursionLimit,它们对应着JsonResult的同名属性。MaxJsonLength限制了被反序列化和序列化生成的JSON字符串的长度,默认值位为2097152(0x200000,等同于 4 MB 的 Unicode 字符串数据)。RecursionLimit用于设置被序列化对象和反序列化生成对象结构的允许的层级数,默认值为100。定义在JsonResult的ExecuteResult方法中通过JavaScriptSerializer对数据对象的序列化,并将序列化生成的JSON字符串作为内容对请求进行响应,具体的逻辑基本上可以通过下面的代码片断来体现。
1: public class JsonResult : ActionResult
2: {
3: //其他成员
4: public override void ExecuteResult(ControllerContext context)
5: {
6: //确认是否用于响应HTTP-GET请求
7: if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Compare(context.HttpContext.Request.HttpMethod, "GET", true) == 0)
8: {
9: throw new InvalidOperationException();
10: }
11:
12: HttpResponseBase response = context.HttpContext.Response
13: //设置媒体类型和编码方式
14: response.ContentType = string.IsNullOrEmpty(this.ContentType) ?"application/json" : this.ContentType;
15: if (this.ContentEncoding != null)
16: {
17: response.ContentEncoding = this.ContentEncoding;
18: }
19:
20: //创建JavaScriptSerializer将数据对象序列化成JSON字符串并写入当前HttpResponse
21: if (null == this.Data)return;
22: JavaScriptSerializer serializer = new JavaScriptSerializer()
23: {
24: MaxJsonLength = this.MaxJsonLength.HasValue ? this.MaxJsonLength.Value : 0x200000,
25: RecursionLimit = this.RecursionLimit.HasValue ? this.RecursionLimit.Value : 100
26: };
27: response.Write(serializer.Serialize(this.Data));
28: }
29: }
在抽象类Controller同样定义如下一系列的Json方法用于根据指定的数据对象、编码方式以及JsonRequestBehavior来创相应的JsonResult。
1: public abstract class Controller : ControllerBase,...
2: {
3: //其他成员
4: protected internal JsonResult Json(object data);
5: protected internal JsonResult Json(object data, string contentType);
6: protected internal JsonResult Json(object data, JsonRequestBehavior behavior);
7: protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding);
8: protected internal JsonResult Json(object data, string contentType, JsonRequestBehavior behavior);
9: protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior);
10: }
了解ASP.NET MVC几种ActionResult的本质:EmptyResult & ContentResult
了解ASP.NET MVC几种ActionResult的本质:FileResult
了解ASP.NET MVC几种ActionResult的本质:JavaScriptResult & JsonResult
了解ASP.NET MVC几种ActionResult的本质:HttpStatusCodeResult & RedirectResult/RedirectToRouteResult
Reference from : http://www.cnblogs.com/artech/archive/2012/08/15/action-result-03.html
了解ASP.NET MVC几种ActionResult的本质:JavaScriptResult & JsonResult的更多相关文章
- 了解ASP.NET MVC几种ActionResult的本质:HttpStatusCodeResult & RedirectResult/RedirectToRouteResult
在本系列的最后一篇,我们来讨论最后三个ActionResult:HttpStatusCodeResult.RedirectResult和RedirectToRouteResult .第一个用于实现针对 ...
- 通过源码了解ASP.NET MVC 几种Filter的执行过程 在Winform中菜单动态添加“最近使用文件”
通过源码了解ASP.NET MVC 几种Filter的执行过程 一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神 ...
- ASP.NET MVC几种找不到资源的问题解决办法
转自:http://www.cnblogs.com/xyang/archive/2011/11/24/2262003.html 在MVC中,controller中的Action和View中的.csht ...
- 理解ASP.NET MVC中的ActionResult
通常我们在一个ASP.NET MVC项目中创建一个Controller的时候,Index()方法默认的返回类型都是ActionResult,通过查看UML图,ActionResult实际上是一个抽象类 ...
- 通过源码了解ASP.NET MVC 几种Filter的执行过程
一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神的工作,而且很多人觉得平时根本不需要知道这些,会用就行了.其实阅读源 ...
- ASP.NET MVC 四种传值方法
1.后台传值: public class DataController : Controller { // GET: Data public ActionResult Index() { //1 Vi ...
- ASP.NET MVC 几种 Filter 的执行过程源码解析
一.前言 之前也阅读过MVC的源码,并了解过各个模块的运行原理和执行过程,但都没有形成文章(所以也忘得特别快),总感觉分析源码是大神的工作,而且很多 人觉得平时根本不需要知道这些,会用就行了.其实阅读 ...
- asp.net mvc 三种过滤器
前几天面试遇到这个问题,发现不是很了解,学习了下,这里记录下来 经常需要将用户的操作记录到日志中,或者是验证用户是否登录了网站, 面对这样的需求,以前的操作是自定义一个统一的全局方法,然后做处理, 在 ...
- ASP.NET MVC 四种Controller向View传值方法
控制器: // Get: Data public ActionResult Index() { //ViewData 方式 ViewData["UserName"] = " ...
随机推荐
- SpringMVC从入门到精通之第三章
上一章节中首先讲解前端控制器DispatcherServlet的配置包括加载springmvc文件.拦截什么样的请求等还有两个组件:分别是适配器和映射器(另外再补充一组) 非注解的处理器映射器 处理器 ...
- 最短路径问题的Dijkstra和SPFA算法总结
Dijkstra算法: 解决带非负权重图的单元最短路径问题.时间复杂度为O(V*V+E) 算法精髓:维持一组节点集合S,从源节点到该集合中的点的最短路径已被找到,算法重复从剩余的节点集V-S中选择最短 ...
- 利用注解进行sql反射代码示例
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Table { String val ...
- [No000001]一切都是最好的安排
<一切都是最好的安排> 从前有一个国家,地不大,人不多,但是人民过着悠闲快乐的生活,因为他们有一位不喜欢做事的国王和一位不喜欢做官的宰相. 国王没有什么不良嗜好,除了打猎以外,最喜欢与宰相 ...
- php一句话后门过狗姿势万千之传输层加工【三】
既然木马已就绪,那么想要利用木马,必然有一个数据传输的过程,数据提交是必须的,数据返回一般也会有的,除非执行特殊命令. 当我们用普通菜刀连接后门时,数据时如何提交的,狗狗又是如何识别的,下面结合一个实 ...
- toodifficult 题解
名字听起来十分厉害啊...一道lzz的提交答案题. 提答题,我们看看题目,给出一个解密程序,叫你加密. 每个点有一个加密的sample和一些要加密的文本. 从题目中我们可以得到一些信息: 加密后一般为 ...
- 全面解读python web 程序的9种部署方式
转载自鲁塔弗的博客,本文地址http://lutaf.com/141.htm python有很多web 开发框架,代码写完了,部署上线是个大事,通常来说,web应用一般是三层结构 web serve ...
- ESXi 6.0 配置
ESXi 6.0 添加静态路由 首先打开ESXi的SSH服务, 在Configuration -> Security Profile -> Services , start SSH 用管理 ...
- Html5 Egret游戏开发 成语大挑战(八)一般性二级页面处理
在游戏中,我们一般会有各种各样的二级页面,比如游戏暂停界面或者游戏结束界面,这些界面组成了对玩家交互主要手段,在游戏开发中,对于这些界面的coding组织是非常有学问的,如果倒退到十年前,游戏开发的老 ...
- java动态代理浅析
最近在公司看到了mybatis与spring整合中MapperScannerConfigurer的使用,该类通过反向代理自动生成基于接口的动态代理类. 于是想起了java的动态代理,然后就有了这篇文章 ...