ASP.NET MVC ActionResult本质:JavaScriptResult & JsonResult
在《ASP.NET MVC ActionResult本质:EmptyResult & ContentResult》和《ASP.NET MVC ActionResult本质:FileResult》剖析了EmptyResult、ContentResult和FileResult这三种ActionResult是如何将Action执行的结果响 应给客户端的。本篇文章着重介绍在进行Ajax调用中经常使用的两个ActionResult,即JavaScriptResult和 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: }
作者:Artech
出处:http://artech.cnblogs.com/
加支付宝好友偷能量挖...