瞭解ASP.NET MVC幾種ActionResult的本質:JavaScriptResult & JsonResult

[轉載]http://www.cnblogs.com/artech/archive/2012/08/15/action-result-03.html

在之前的兩篇文章(《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>

運行我們的程序後,一個包含三個商品的購物車信息會被呈現出來,當我們輸入相應的訂購數量並點擊“提交訂單”後,訂單處理結果消息會彈出來。下圖所示的就是庫存不足的情況下顯示的消息。

image

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



發佈了29 篇原創文章 · 獲贊 25 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章