前面閱讀第六波導(dǎo)航: ASP.NET MVC4 IN ACTION學(xué)習(xí)筆記-第六波[Ajax in ASP.NET MVC][1/3] ASP.NET MVC4 IN ACTION學(xué)習(xí)筆記-第六波[Ajax in ASP.NET MVC][2/3] 原著:ASP.NET MVC 4 IN ACTION 大家好! 我是茗洋芳竹,首先聲明,我不是一個(gè)翻譯人員,我是個(gè)90后程序員. 夏天到了,哎,,人也變懶了,,開始寫吧… 讀者約定: ”網(wǎng)頁標(biāo)簽 element“代表 網(wǎng)頁上的一個(gè)DOM元素,例如 div element就是網(wǎng)頁中有個(gè)div的DOM元素 7.3 Ajax返回JSON 和 客戶端模版的使用 前面幾節(jié)都是對Ajax請求返回HTML,,雖然用這種方法沒有任何錯誤,,但是你沒有簡化HTML的返回,其實(shí)你可以返回任意種格式的數(shù)據(jù),例如,,純文本(plain text),,XML,JSON,。在下面一節(jié)我們將使用通過Ajax返回JSON的模式寫程序
7.3.1 Ajax with JSON JSON全稱:JavaScript Object Notation 它提供了一種很簡單的方式表達(dá)數(shù)據(jù),如果你熟悉JavaScript Object,,你就會很容易熟悉JSON,。 兩種表達(dá)數(shù)據(jù)的方式的比較 XML數(shù)據(jù): <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格式
如果你熟悉C#中的集合,那你就很快會用JSON,,通過鍵,,獲得對象,然后你自己再操作對象就行了,,這里不多說了 為了演示JSON,,我們添加一個(gè)SpeakerController,然后在Models文件夾下添加一個(gè)Speaker實(shí)體類: 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: }
再手動創(chuàng)建一個(gè)SpeakerRepository倉庫類,,因?yàn)槲覀儧]有使用到數(shù)據(jù)庫,,我們添加的數(shù)據(jù)都在內(nèi)存里面,真實(shí)的項(xiàng)目都是使用數(shù)據(jù)庫,,這里用于演示用的,。在model的構(gòu)造函數(shù)中,創(chuàng)造一個(gè)SpeakerRepository集合,,初始化數(shù)據(jù),,代碼如下: 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: }
我們再在這里面寫幾個(gè)獲得數(shù)據(jù)的方法 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: }
最后我們處理那個(gè)SpeakerController 在Index action中添加如下代碼,返回所有的Speaker信息,,添加對應(yīng)的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: }
這個(gè)方法,,用于首頁顯示Speaker信息,然后單擊一項(xiàng),,我們要顯示詳情,,那么我們添加一個(gè)Details action,參數(shù)為id 代碼如下: 1: public ActionResult Details(int id) 2: {
3: var speaker = _repository.FindSpeaker(id);
4: return Json(speaker, JsonRequestBehavior.AllowGet); 5: }
看的話,,估計(jì)你也懂了,,但是我還是要講一下JSON方法,更深入一下,,比如為什么后面加了個(gè)參數(shù),,其實(shí)可以不要的
這里,我們使用了Id從倉庫里獲得 Speaker的信息,,通過Controller的JSON方法可以將一個(gè)對象序列化成JSON,。這個(gè)方法返回一個(gè)JSONResult,JSONResult是實(shí)現(xiàn)ActionResult的,當(dāng)開始把一個(gè)對象序列化成一個(gè)JSON的時(shí)候,,它會把結(jié)果寫入 result stream(結(jié)果流)里面,,就可以返回信息了。 ASP.NET MVC,JSON和GET請求 你也注意到了,,在上面的代碼,,Controller中的JSON方法的參數(shù),我們使用了一個(gè)枚舉值JsonRequestBehavior.AllowGet,。默認(rèn)的JsonResult都是以Post方式響應(yīng),,如果你想以Get方式,就這樣用這個(gè)行為,。 在這里用這個(gè)行為可以避免 JSON hijacking(劫持,是一種跨站點(diǎn)的腳本的form) 如果一個(gè)站點(diǎn)去響應(yīng)Get請求,,返回的JSON數(shù)據(jù)含有敏感數(shù)據(jù),那么一個(gè)惡意的網(wǎng)站就可以在一個(gè)敏感的位置(容易攻擊的位置,,適合壞人下手的位置)通過嵌入腳本可以潛在地讓不知情的用戶看到他們(壞人)的數(shù)據(jù),。 如果一個(gè)認(rèn)證了的用戶訪問了這個(gè)惡網(wǎng)站,這些數(shù)據(jù)就會被下載,,然后惡意的網(wǎng)站就可以得到它,,我們將會在下一波講 JSON hihacking 在我們的特殊的例子中,我們不會返回敏感的數(shù)據(jù),,所以讓JSON以GET請求訪問絕對安全,。 接下來我們看下Index view,要使用到的一些圖片下載,,點(diǎn)擊立即下載 下載好后,,放到Content文件夾下(css文件提供,因?yàn)橹饕vMVC) 我們先建立一個(gè)空的Speakers.js,,用于寫js代碼 關(guān)于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>
我們后臺傳過來一個(gè)list,,視圖中標(biāo)記了Model,使得視圖變成一個(gè)強(qiáng)類型視圖,,我們使用foreach循環(huán),,顯示展示結(jié)果,每個(gè)結(jié)果添加超鏈接,,單擊顯示詳情,。 我們使用一個(gè)div element,顯示單擊后的詳情信息,我們現(xiàn)在不使用,,我們將在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,這點(diǎn)絕對不是問題 getJSON方法會自動將JSON string轉(zhuǎn)換成JavaScript對象,。 運(yùn)行項(xiàng)目,,效果圖:
光顯示一個(gè) FirstName是沒有用的,接下來我們在頁面中添加更多的標(biāo)簽,來顯示說話者的詳細(xì)信息,,還有照片,。 下面我們講客戶端模版。
7.3.2 Client-side templates 我們在服務(wù)端大多都是使用Razor文件作為模版,,我們也可以在客戶端創(chuàng)建一個(gè)模版 客戶端模版 允許我們不需要返回服務(wù)器或者通過JavaScript構(gòu)建elements就可以立即在瀏覽器中立即生成markup(標(biāo)簽,,網(wǎng)頁中的元素),我們有幾種客戶端模版庫可供我們選擇,,但是我們使用jQuery-tmpl ,, 一個(gè)jQuery寫的模版庫,微軟寫的,,已經(jīng)貢獻(xiàn)到j(luò)Query 項(xiàng)目,開源,。 我們修改speaker列表,,讓speaker name可以click,然后他們的bio(個(gè)人簡歷)和照片將會顯示: 為了引用jQuery-tmpl,我們可以從https://github.com/jquery/jquery-tmpl 下載,,然后把他們放到我們項(xiàng)目的scripts文件夾里面,。或者我們可以直接引用來自 Microsoft’s CDN 的 http:// ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.js,,一旦引用好了,,我們就可以在view中添加 模版了 點(diǎn)擊直接下載jquery.tmpl.js 注意,這個(gè)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腳本,,然后生命了一個(gè)模版,模版在一個(gè)script element中,,用的是type=”text/x-jquery-tmpl”,把template的標(biāo)簽放到一個(gè)script element中可以保證,,頁面載入時(shí),不會直接顯示模版,。 我們的模版包括了照片和個(gè)人簡歷的信息,,我們引用JSON對象,通過使用${}代碼塊(nuget)來包裹(wrap)它們,,這些在模版呈現(xiàn)的時(shí)候,,會被替換的。下面我們需要修改Speakers.js中的JavaScript來呈現(xiàn)這個(gè)模版,。 修改的代碼如下: 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: });
這里代碼只有一點(diǎn)點(diǎn)不同,,第5行,每次查看詳情的時(shí)候,,先清空原來的數(shù)據(jù) 第13行,,模版的位置 點(diǎn) tmpl(JSON轉(zhuǎn)換過的JavaScript對象)點(diǎn) appendTo(在哪里顯示)
7.3.3 完成 這個(gè)例子 這個(gè)例子已經(jīng)大大地完成了,但是還有個(gè)缺點(diǎn)。如果JavaScript禁用了,,單擊的時(shí)候,,JSON就作為文件下載了,而不是呈現(xiàn)一個(gè)模版,。 為了完成這個(gè),,我們可以一個(gè)最簡單的方法,修改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,我們還可以使用一個(gè)action method selector,,用于Ajax和non-Ajax請求,。在第一波我們已經(jīng)看到了 action selector怎么使用的了,我們可以創(chuàng)建一個(gè)繼承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: }
如果當(dāng)前的請求是通過Ajax請求的,,IsValidForRequest方法就會返回true 現(xiàn)在我們在SpeakersController中使用這個(gè)attribute,,通過定義兩個(gè)action-----一個(gè)是Ajax請求,另一個(gè)是正常的請求 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: }
第一個(gè)重載的Details action被我們創(chuàng)建的AcceptAjaxAttribute注解(annotated)了,。這個(gè)保證了這個(gè)方法只能被Ajax方法請求到,,這個(gè)版本的action會返回JSON序列化后的speaker數(shù)據(jù)。 另一個(gè)重載的沒有使用AcceptAjaxAttribute,,這就意味著它將可以被非Ajax請求訪問,,這個(gè)action簡單的傳遞了個(gè)speaker實(shí)例對象給視圖,注意,,C#中不能定義同名字的相同簽名的兩個(gè)方法,,這第二個(gè)版本的action被命名為Details_NonAjax,但是他依然可以被URL/Speakers/Details訪問,,因?yàn)樗籄ctionName注解了,。 這個(gè)AcceptAjaxAttribute作為ASP.NET MVC Futures DLL的一部分可以被找到,您可以通過http://aspnet. 在這個(gè)特殊的例子中,,使用AcceptAjaxAttribute不會帶來太多的方便,,但是在一個(gè)action 表單Ajax和非Ajax處理的操作是不一樣的情形的時(shí)候,把處理的代碼分開寫,,會具有可讀性,。 我們也可以定義一個(gè)視圖,針對非Ajax版本的action,。這個(gè)視圖顯示speaker的詳細(xì)信息,,很多跟客戶端模版一樣 我們添加 Details_NonAjax對應(yīng)的視圖; 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代碼 在頂部重新引用 一個(gè)jquery庫
運(yùn)行項(xiàng)目,效果如下:
代碼下載:http://download.csdn.net/download/yangyanghaoran/5393947
|
|