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