好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

了解ASP.NET MVC几种ActionResult的本质:JavaScriptResult &a

了解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

作者: Artech
出处: http://artech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类:  [01] 技术剖析

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于了解ASP.NET MVC几种ActionResult的本质:JavaScriptResult &a的详细内容...

  阅读:38次