ASP.NET MVC4 IN ACTION学习笔记-第六波[Ajax in ASP.NET MVC][3/3]

前面阅读第六波导航:

ASP.NET MVC4 IN ACTION学习笔记-第六波[Ajax in ASP.NET MVC][1/3]

ASP.NET MVC4 IN ACTION学习笔记-第六波[Ajax in ASP.NET MVC][2/3]

image         原著:ASP.NET MVC 4 IN ACTION

         大家好!

         我是茗洋芳竹,首先声明,我不是一个翻译人员,我是个90后程序员.

         夏天到了,哎,人也变懒了,开始写吧…

         读者约定:

         ”网页标签 element“代表 网页上的一个DOM元素,例如 div element就是网页中有个div的DOM元素

7.3 Ajax返回JSON 和 客户端模版的使用

      前面几节都是对Ajax请求返回HTML,虽然用这种方法没有任何错误,但是你没有简化HTML的返回,其实你可以返回任意种格式的数据,例如,纯文本(plain text),XML,JSON。在下面一节我们将使用通过Ajax返回JSON的模式写程序

 

 

 

7.3.1 Ajax with JSON

        JSON全称:JavaScript Object Notation

        它提供了一种很简单的方式表达数据,如果你熟悉JavaScript Object,你就会很容易熟悉JSON。

        两种表达数据的方式的比较

       XML数据:

   <Speaker> 
         <Id>5</Id> 
         <FirstName>Jeremy</FirstName> 
         <LastName>Skinner</LastName> 
         <PictureUrl>/content/jeremy.jpg</PictureUrl> 
         <Bio>Jeremy Skinner is a C#/ASP.NET software developer in the UK.</Bio> 
       </Speaker> 

      JSON格式
          {
             "Id":5, "FirstName":"Jeremy"
             "LastName":"Skinner",
             "PictureUrl":"/content/jeremy.jpg",
            "Bio":"Jeremy Skinner is a C#/ASP.NET software developer in the UK."
           }

  

如果你熟悉C#中的集合,那你就很快会用JSON,通过键,获得对象,然后你自己再操作对象就行了,这里不多说了

为了演示JSON,我们添加一个SpeakerController,然后在Models文件夹下添加一个Speaker实体类:

   1:   public class Speaker
   2:      {
   3:          public int Id { get; set; }        
   4:          public string FirstName { get; set;}
   5:          public string LastName { get; set; }
   6:          public string PictureUrl { get; set; }
   7:          public string Bio { get; set; }
   8:          public string FullName
   9:          {
  10:              get { return string.Format("{0} {1}", FirstName, LastName); }
  11:          }
  12:      }

 

再手动创建一个SpeakerRepository仓库类,因为我们没有使用到数据库,我们添加的数据都在内存里面,真实的项目都是使用数据库,这里用于演示用的。在model的构造函数中,创造一个SpeakerRepository集合,初始化数据,代码如下:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web;
   5:   
   6:  namespace AjaxExamples.Models
   7:  {
   8:      public class SpeakerRepository
   9:      {
  10:          private static readonly List<Speaker> _speakers = new List<Speaker>();
  11:   
  12:          static SpeakerRepository()
  13:          {
  14:              _speakers = new List<Speaker> {
  15:   
  16:                 new Speaker
  17:                  {
  18:                      Id = 1,
  19:                      FirstName = "Jimmy",
  20:                      LastName = "Bogard",
  21:                      PictureUrl = "/content/jimmy.png",
  22:                      Bio = "Jimmy is a Principal Consultant at Headspring Systems in Austin, TX.",
  23:                  },
  24:                  new Speaker
  25:                  {
  26:                      Id = 2,
  27:                      FirstName = "Jeffrey",
  28:                      LastName = "Palermo",
  29:                      PictureUrl = "/content/jeffrey.jpg",
  30:                      Bio = "Jeffrey Palermo is a Microsoft MVP and Chief Technology Officer of Headspring Systems in Austin, TX.",
  31:                  },
  32:                  new Speaker
  33:                  {
  34:                      Id = 3,
  35:                      FirstName = "Eric",
  36:                      LastName = "Hexter",
  37:                      PictureUrl = "/content/eric.jpg",
  38:                      Bio = "Eric Hexter is an Enterprise Architect at Dell in Austin, TX.",
  39:                  },
  40:                  new Speaker
  41:                  {
  42:                     Id = 4,
  43:                     FirstName = "Matt",
  44:                     LastName = "Hinze",
  45:                     PictureUrl = "/content/matt.jpg",
  46:                     Bio = "Matt Hinze is a Principal Consultant at Headspring Systems in Austin, TX.",
  47:                  },
  48:   
  49:                 new Speaker
  50:                  {
  51:                      Id = 5,
  52:                      FirstName = "Jeremy",
  53:                      LastName = "Skinner",
  54:                      PictureUrl = "/content/jeremy.jpg",
  55:                      Bio = "Jeremy Skinner is a C#/ASP.NET software developer in the UK.",
  56:                  }
  57:              };
  58:   
  59:   
  60:          }
  61:   
  62:   
  63:   
  64:      }
  65:  }

我们再在这里面写几个获得数据的方法

   1:      public IEnumerable<Speaker> FindAll()
   2:          {
   3:              return _speakers;
   4:          }
   5:   
   6:          public Speaker FindSpeaker(int id)
   7:          {
   8:              return _speakers.SingleOrDefault(x => x.Id == id);
   9:          }

最后我们处理那个SpeakerController

在Index action中添加如下代码,返回所有的Speaker信息,添加对应的Index view

   1:    private readonly SpeakerRepository _repository = new SpeakerRepository();
   2:   
   3:          public ActionResult Index()
   4:          {
   5:              var speakers = _repository.FindAll();
   6:              return View(speakers);
   7:          }

这个方法,用于首页显示Speaker信息,然后单击一项,我们要显示详情,那么我们添加一个Details action,参数为id

代码如下:

   1:   public ActionResult Details(int id)
   2:     {
   3:              var speaker = _repository.FindSpeaker(id);
   4:              return Json(speaker, JsonRequestBehavior.AllowGet);
   5:      }

看的话,估计你也懂了,但是我还是要讲一下JSON方法,更深入一下,比如为什么后面加了个参数,其实可以不要的

 

 

这里,我们使用了Id从仓库里获得 Speaker的信息,通过Controller的JSON方法可以将一个对象序列化成JSON。这个方法返回一个JSONResult,JSONResult是实现ActionResult的,当开始把一个对象序列化成一个JSON的时候,它会把结果写入 result stream(结果流)里面,就可以返回信息了。

         ASP.NET MVC,JSON和GET请求

         你也注意到了,在上面的代码,Controller中的JSON方法的参数,我们使用了一个枚举值JsonRequestBehavior.AllowGet。默认的JsonResult都是以Post方式响应,如果你想以Get方式,就这样用这个行为。

在这里用这个行为可以避免 JSON hijacking(劫持,是一种跨站点的脚本的form)

         如果一个站点去响应Get请求,返回的JSON数据含有敏感数据,那么一个恶意的网站就可以在一个敏感的位置(容易攻击的位置,适合坏人下手的位置)通过嵌入脚本可以潜在地让不知情的用户看到他们(坏人)的数据。

         如果一个认证了的用户访问了这个恶网站,这些数据就会被下载,然后恶意的网站就可以得到它,我们将会在下一波讲 JSON hihacking

在我们的特殊的例子中,我们不会返回敏感的数据,所以让JSON以GET请求访问绝对安全。

接下来我们看下Index view,要使用到的一些图片下载,点击立即下载    下载好后,放到Content文件夹下(css文件提供,因为主要讲MVC)

我们先建立一个空的Speakers.js,用于写js代码

关于Index view代码如下:

   1:  @model IEnumerable<AjaxExamples.Models.Speaker>
   2:  @{
   3:      ViewBag.Title = "JSON Example";
   4:  }
   5:  @section head {
   6:      <link rel="Stylesheet" type="text/css" href="@Url.Content("~/content/speakers.css")" />
   7:      <script type="text/javascript" src="@Url.Content("~/scripts/Speakers.js")"></script>
   8:  }
   9:   
  10:  <h2>Speakers</h2>
  11:   
  12:  <ul class="speakers">
  13:      @foreach (var speaker in Model) {
  14:      <li>
  15:          @Html.ActionLink(speaker.FullName, "Details", new { id = speaker.Id })
  16:      </li>
  17:      }
  18:  </ul>
  19:   
  20:  <img id="indicator" src="@Url.Content("~/content/load.gif")" alt="loading..." style="display:none" />
  21:   
  22:  <div class="selected-speaker" style="display:none">
  23:  </div>

我们后台传过来一个list,视图中标记了Model,使得视图变成一个强类型视图,我们使用foreach循环,显示展示结果,每个结果添加超链接,单击显示详情。

我们使用一个div element,显示单击后的详情信息,我们现在不使用,我们将在7.3.2中使用

接下来我们在Speakers.js文件中写入Js

   1:  $(document).ready(function () {
   2:      $("ul.speakers a").click(function (e) {
   3:          e.preventDefault();
   4:          $("#indicator").show();
   5:          var url = $(this).attr('href');
   6:          $.getJSON(url, null, function (speaker) {
   7:              $("#indicator").hide();
   8:              alert(speaker.FirstName);
   9:          });
  10:      });
  11:  });

$.getJSON方法具体参考 Jquery手册,如果你使用过jquery,这点绝对不是问题

getJSON方法会自动将JSON string转换成JavaScript对象。

运行项目,效果图:

9

 

光显示一个 FirstName是没有用的,接下来我们在页面中添加更多的标签,来显示说话者的详细信息,还有照片。

下面我们讲客户端模版。

 

7.3.2 Client-side templates

         我们在服务端大多都是使用Razor文件作为模版,我们也可以在客户端创建一个模版

         客户端模版 允许我们不需要返回服务器或者通过JavaScript构建elements就可以立即在浏览器中立即生成markup(标签,网页中的元素),我们有几种客户端模版库可供我们选择,但是我们使用jQuery-tmpl 一个jQuery写的模版库,微软写的,已经贡献到jQuery 项目,开源。

我们修改speaker列表,让speaker name可以click,然后他们的bio(个人简历)和照片将会显示:

为了引用jQuery-tmpl,我们可以从https://github.com/jquery/jquery-tmpl 下载,然后把他们放到我们项目的scripts文件夹里面。或者我们可以直接引用来自   Microsoft’s  CDN  的 http:// ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.js,一旦引用好了,我们就可以在view中添加 模版了

点击直接下载jquery.tmpl.js      注意,这个tmpl.js文件一定要放在 Speakers.js上面,注意加载顺序

 

  <script type="text/javascript" src="@Url.Content("~/scripts/jquery.tmpl.js")">  </script> 
   1:  <br style="clear: both;" />
   2:  <script id="speakerTemplate" type="text/x-jquery-tmpl">
   3:      <img src="${PictureUrl}"
   4:          alt="Speaker image" class="speaker-pic" />
   5:      <p class="speaker-bio">
   6:          ${Bio}                             
   7:      </p>
   8:   
   9:   <br style="clear: both;" />
  10:  </script>

 

这里我们引用了 jQuery-tmpl脚本,然后生命了一个模版,模版在一个script element中,用的是type=”text/x-jquery-tmpl”,把template的标签放到一个script element中可以保证,页面载入时,不会直接显示模版。

我们的模版包括了照片和个人简历的信息,我们引用JSON对象,通过使用${}代码块(nuget)来包裹(wrap)它们,这些在模版呈现的时候,会被替换的。下面我们需要修改Speakers.js中的JavaScript来呈现这个模版。

修改的代码如下:

   1:  $(document).ready(function () {
   2:      $("ul.speakers a").click(function (e) {
   3:          e.preventDefault();
   4:   
   5:          $(".selected-speaker").hide().html('');
   6:          $("#indicator").show();
   7:   
   8:          var url = $(this).attr('href');
   9:   
  10:          $.getJSON(url, null, function (speaker) {
  11:              $("#indicator").hide();
  12:   
  13:              $("#speakerTemplate")
  14:                  .tmpl(speaker)
  15:                  .appendTo('.selected-speaker');
  16:   
  17:              $('.selected-speaker').show();
  18:          });
  19:      });
  20:  });

这里代码只有一点点不同,第5行,每次查看详情的时候,先清空原来的数据

第13行,模版的位置 点 tmpl(JSON转换过的JavaScript对象)点 appendTo(在哪里显示)

 

 

7.3.3 完成 这个例子

       这个例子已经大大地完成了,但是还有个缺点。如果JavaScript禁用了,单击的时候,JSON就作为文件下载了,而不是呈现一个模版。

       为了完成这个,我们可以一个最简单的方法,修改SpeakersController中的 Details action

   1:   public ActionResult Details(int id)
   2:          {
   3:              var speaker = _repository.FindSpeaker(id);
   4:              if (Request.IsAjaxRequest()) {
   5:                  return Json(speaker, JsonRequestBehavior.AllowGet);
   6:              }
   7:              return View(speaker);
   8:          }

除了依靠在我们的代码中使用if,我们还可以使用一个action method selector,用于Ajax和non-Ajax请求。在第一波我们已经看到了 action selector怎么使用的了,我们可以创建一个继承ActionMethodSelector的AcceptAjaxAttribute

我们在Filters文件下建立

image

代码如下:

   1:  using System;
   2:  using System.Reflection;
   3:  using System.Web.Mvc;
   4:   
   5:  namespace AjaxExamples.Filters
   6:  {
   7:      [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
   8:      public class AcceptAjaxAttribute : ActionMethodSelectorAttribute
   9:      {
  10:          public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
  11:          {
  12:              return controllerContext.HttpContext.Request.IsAjaxRequest();
  13:          }
  14:      }
  15:  }

image

如果当前的请求是通过Ajax请求的,IsValidForRequest方法就会返回true

现在我们在SpeakersController中使用这个attribute,通过定义两个action-----一个是Ajax请求,另一个是正常的请求

   1:     public ActionResult Details(int id)
   2:          {
   3:              var speaker = _repository.FindSpeaker(id);
   4:              if (Request.IsAjaxRequest()) {
   5:                  return Json(speaker, JsonRequestBehavior.AllowGet);
   6:              }
   7:              return View(speaker);
   8:          }
   9:   
  10:          [ActionName("Details")]
  11:          public ActionResult Details_NonAjax(int id)
  12:          {
  13:              var speaker = _repository.FindSpeaker(id);
  14:              return View(speaker);
  15:          }

第一个重载的Details action被我们创建的AcceptAjaxAttribute注解(annotated)了。这个保证了这个方法只能被Ajax方法请求到,这个版本的action会返回JSON序列化后的speaker数据。

另一个重载的没有使用AcceptAjaxAttribute,这就意味着它将可以被非Ajax请求访问,这个action简单的传递了个speaker实例对象给视图,注意,C#中不能定义同名字的相同签名的两个方法,这第二个版本的action被命名为Details_NonAjax,但是他依然可以被URL/Speakers/Details访问,因为它被ActionName注解了。

这个AcceptAjaxAttribute作为ASP.NET MVC Futures DLL的一部分可以被找到,您可以通过http://aspnet.codeplex.com

在这个特殊的例子中,使用AcceptAjaxAttribute不会带来太多的方便,但是在一个action 表单Ajax和非Ajax处理的操作是不一样的情形的时候,把处理的代码分开写,会具有可读性。

我们也可以定义一个视图,针对非Ajax版本的action。这个视图显示speaker的详细信息,很多跟客户端模版一样

我们添加 Details_NonAjax对应的视图;

   1:  @model AjaxExamples.Models.Speaker
   2:   
   3:  @section head {
   4:      <link rel="Stylesheet" type="text/css" href="@Url.Content("~/content/speakers.css")" />
   5:  }
   6:  <h2>Speaker Details: @Model.FullName</h2>
   7:   
   8:  <p class="speaker">
   9:      <img src="@Model.PictureUrl"                          
  10:                    alt="@Model.FullName" />
  11:      <span class="speaker-bio">@Model.Bio</span>
  12:  </p>
  13:   
  14:  <br style="clear: both" />
  15:  @Html.ActionLink("Back to speakers", "index")

 

Details中的代码如下:

   1:  @model AjaxExamples.Models.Speaker
   2:  @{
   3:      ViewBag.Title = "Speaker Details";
   4:  }
   5:  @section head {
   6:      <link rel="Stylesheet" type="text/css" href="@Url.Content("~/content/speakers.css")" />
   7:      <style type="text/css">
   8:          .speaker img 
   9:          {
  10:              float: left;
  11:              margin: 5px;
  12:          }
  13:           
  14:      </style>
  15:  }
  16:   
  17:  <h2>Speaker Details: @Model.FullName</h2>
  18:   
  19:  <p class="speaker">
  20:      <img src="@Model.PictureUrl" alt="@Model.FullName" />
  21:      <p class="speaker-bio">@Model.Bio</p>
  22:  </p>
  23:   
  24:  <br style="clear:both" />
  25:  @Html.ActionLink("Back to speakers", "index")

 

我们注释掉 Views/Shared/_Layout.cshtml中底部的一行jquery代码

在顶部重新引用 一个jquery库

 

运行项目,效果如下:

 

 代码下载:http://download.csdn.net/download/yangyanghaoran/5393947

 

 

posted @ 2013-05-17 09:12  AYUI框架  阅读(2597)  评论(10编辑  收藏  举报