前面閱讀第六波導航:
ASP.NET MVC4 IN ACTION學習筆記-第六波[Ajax in ASP.NET MVC][1/3]
ASP.NET MVC4 IN ACTION學習筆記-第六波[Ajax in ASP.NET MVC][2/3]
原著: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對象。
運行項目,效果圖:
光顯示一個 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文件下建立
代碼如下:
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: }
如果當前的請求是通過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