了解ASP.NET MVC几种ActionResult的本质:JavaScriptResult & JsonResult2012-09-05 博客园 Artech在之前的两篇文章(《》和《》)我们剖析了EmptyResult、ContentResult和FileResult这三种ActionResult是如何将Action执行的结果响应给客户端的。本篇文章着重介绍在进行Ajax调用中经常使用的两个ActionResult,即JavaScriptResult和JsonResult。一、JavaScriptResultJavaScriptResult使我们可以在服务端动态地生成一段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 intQuantity { 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: }