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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM