在之前的兩篇文章(《EmptyResult & ContentResult》和《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: }