zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    python3--字符串
    python3--数字运算,取数
    全栈项目|小书架|服务器开发-用户模块设计(用户表设计,注册登录,退出登录)
    全栈项目|小书架|服务器开发-NodeJS 使用 JWT 实现登录认证
    全栈项目|小书架|服务器开发-JWT 详解
    全栈项目|小书架|服务器开发-Koa2中间件机制洋葱模型了解一下
    全栈项目|小书架|服务器开发-NodeJS 中使用 Sequelize 操作 MySQL数据库
    全栈项目|小书架|服务器开发-Koa2 连接MySQL数据库(Navicat+XAMPP)
    全栈项目|小书架|服务器开发-Koa2 参数校验处理
    全栈项目|小书架|服务器开发-Koa2 全局异常处理
  • 原文地址:https://www.cnblogs.com/AaronYang/p/3083082.html
Copyright © 2011-2022 走看看