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>

  运行我们的程序后,一个包含三个商品的购物车信息会被呈现出来,当我们输入相应的订购数量并点击“提交订单”后,订单处理结果消息会弹出来。下图所示的就是库存不足的情况下显示的消息。

ASP.NET MVC ActionResult本质:JavaScriptResult & JsonResult

三、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/

加支付宝好友偷能量挖...


评论(0)网络
阅读(128)喜欢(0)asp.net-mvc