精通ASP.NET MVC ——URL和Ajax辅助器方法
Ajax(或者,如果你愿意,也可以稱為AJAX)是 Asynchronous JavaScripts and XML(異步JavaScripts與XML)。其XML部分并不如它往常那樣意義重大,但是異步部分卻使AJax十分有用。這是后臺請求服務器數據,而不必重載Web頁面的一種模型。MVC框架包含了對漸進式Ajax內建的支持,這意味著你將使用輔助器方法來定義AJax特性,而不必在整個視圖中添加代碼。
準備示例項目?
新建一個空的MVC項目,然后新建一個People控制器,代碼如下圖所示:
public class PeopleController : Controller{private Person[] personData = {new Person { FristName = "Admin",LastName = "FreeMan",Role = Role.Admin},new Person { FristName = "Jacqui",LastName = "Griffyth",Role = Role.User},new Person { FristName = "John",LastName = "Smith",Role = Role.User},new Person { FristName = "Anne",LastName = "Jones",Role = Role.Guset}};// GET: Peoplepublic ActionResult Index(){return View();}public ActionResult GetPeople(){return View(personData);}[HttpPost]public ActionResult GetPeople(string selectedRole){if (selectedRole == null || selectedRole == "ALL"){return View(personData);}else{Role selected = (Role)Enum.Parse(typeof(Role), selectedRole);return View(personData.Where(p => p.Role == selected));}}}在Views/Shared/_Layout.cshtml中添加CSS樣式,?如下圖所示:
@{Layout = null; }<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>_Layout</title><style typeof="text/css">label {display :inline-block;width:100px;}div.dataELem {margin: 5px;}h2 > label {width:inherit;}.editor-label, .editor_field {float:left;margin-top:10px;}.editor_field input {height:20px;}.editor-label {clear:left;}.editor_field {margin-left:10px;}.input[type=submit] {float:left;clear:both;margin-top:10px;}.column {float:left;margin:10px;}table, td, th {border:thin solid black;border-collapse:collapse;padding:5px;background-color:lemonchiffon; text-align:left;margin:10px 0;}div.load {color:red;margin:10px;font-weight:bold;}div.ajaxLink {margin-top :10px;margin-right:5px;float:left;}</style> </head> <body><div> </div>@RenderBody() </body> </html>創建同步表單,/Views/People/GetPeople.cshtml,代碼如下圖所示:
@using WebApplication1.Models; @model IEnumerable<Person>@{ViewBag.Title = "GetPeople";Layout = "/Views/Shared/_Layout.cshtml"; }<h2>Get People </h2><table><thead><tr>First</tr><tr>Last</tr><tr>Role</tr></thead><tbody>@foreach (Person p in Model){<tr><td>@p.FristName</td><td>@p.LastName</td><td>@p.Role</td></tr>}</tbody> </table>@using (Html.BeginForm()) {<div>@Html.DropDownList("selectedRole",new SelectList(new[] { "ALL"}.Concat(Enum.GetNames(typeof(Role)))))<button type="submit">submit</button></div> }?運行程序,并導航到/People/Getpeople/,如下圖所示:
? ? ? ? ? ? ? ? ?
選擇下拉框中的角色類型,并點擊submit按鈕后,可以篩選出人員菜單,如下圖所示:
? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ???
? ? ? ? ? ? ? ???
創建基本的鏈接和URL?
視圖的最基本任務之一是創建鏈接或URL,使用戶能夠隨之進入應用程序的其他部分。以下是常見的可用的HTML輔助器:
| 描述 | 示例 |
| 相對于應用程序的URL | Url.Content(" / Content / Site.css") output: /Content/Site.css |
| 鏈接到指定動作/控制器 | Html.ActionLink("My Link","Index","Home") output: <a href = "/"> My Link </a> |
| 動作URL | Url.Action("GetPeople","Peopole") output: /People/Getpeople |
| 使用路由數據的URL | Url.RouteUrl(new {controller = "People",action = "GetPeople"}) output: /People/Getpeople |
| 使用路由數據的鏈接 | Html.Route("My Link",new { controller = "People",action = "GerPeople"}) output: <a href = "/People/GetPeople">My Link</a> |
| 鏈接到指定的路由 | Html.RouteLink("My Link","FormRoute",new { controller = "People", action = "GetPeople"}) output: <a href = "/app/fomrs/People/GetPeople">My Link </a> |
為Index控制器,添加了Index.html視圖文件,代碼如下圖所示:?
@{ViewBag.Title = "Index";Layout = "/View/Shared/_Layout.cshtml"; }<h2>Basci Links</h2> <table><thead><tr><th>Helper</th><tr>Output</tr></thead><tbody><tr><td>Url.Content("~/Content/Site.css")</td><td>@Url.Content("~/Content/Site.css")</td></tr> <tr><td>Html.ActionLink("My Link","Index","Home")</td><td>@Html.ActionLink("My Link","Index","Home")</td></tr> <tr><td>Url.Action("GetPeople","People")</td><td>@Url.Action("GetPeople","People")</td></tr> <tr><td>Url.RouteUrL(new {controller = "People",action = "GetPeople"})</td><td>@Url.RouteUrl(new { controller = "People",action = "GetPeople"})</td></tr><tr><td>Html.RouteLink("My Link",new { controller = "People",action = "GetPeople"})</td><td>@Html.RouteLink("My Link",new { controller = "People",Action = "GetPeople"})</td></tr><tr><td>Html.RouteLink("My Link","FormRoute",new { controller = "People",action = "GetPeople"})</td><td>@Html.RouteLink("My Link","FormRoute", new { controller = "People",action = "GetPeople"})</td></tr></tbody> </table>并在RouteConfig的RegisterRoutes方法中,新增一條名叫FormRoute的路由,代碼如下圖所示:
routes.MapRoute("FormRoute","app/forms/{controller}/{action}/{id}",defaults:new { controller = "Home",action = "Index",id = UrlParameter.Optional})運行程序,并導航到/home/index,如下圖所示:?
?
使用AJax的條件
1、在Web.config文件中,UnobtrusiveJaveScriptsEnable的屬性條目,必須為true,如下圖所示:
? ? ? ? ? ? ? ? ? ? ? ?
2、需要引入JS文件,一個是JQuery庫文件,另一個是Ajax文件,如下圖所示,在NugGet程序包中搜索下載即可:
? ? ? ?
在_Layout.cshtml中引入這兩個JS文件:?
? ? ? ??
創建漸進式Ajax表單?
我們的目標是,在點擊submit按鈕時,只有HTML table元素中的數據被替換。首先重構People控制器中的方法,如下圖所示:
public class PeopleController : Controller{private Person[] personData = {new Person { FristName = "Admin",LastName = "FreeMan",Role = Role.Admin},new Person { FristName = "Jacqui",LastName = "Griffyth",Role = Role.User},new Person { FristName = "John",LastName = "Smith",Role = Role.User},new Person { FristName = "Anne",LastName = "Jones",Role = Role.Guset}};// GET: Peoplepublic ActionResult Index(){return View();}//public ActionResult GetPeople()//{// return View(personData);//}//[HttpPost]//public ActionResult GetPeople(string selectedRole)//{// if (selectedRole == null || selectedRole == "ALL")// {// return View(personData);// }// else// {// Role selected = (Role)Enum.Parse(typeof(Role), selectedRole);// return View(personData.Where(p => p.Role == selected));// }//}public ActionResult GetPeople(string selectedRole = "All"){return View((object)selectedRole);}public PartialViewResult GetPeopleData(string selectedRole = "All"){IEnumerable<Person> data = personData;if (selectedRole != "All"){Role selected = (Role)Enum.Parse(typeof(Role), selectedRole);data = personData.Where(p => p.Role == selected);}return PartialView(data);}}添加一個對GetPeopleData動作的分部視圖,GetPeopleData.cshtml方法:
@using WebApplication1.Models @model IEnumerable<Person> @{Layout = null; }@foreach (Person p in Model) {<tr><td>@p.FristName</td><td>@p.LastName</td><td>@p.Role</td></tr> }更新GetPeople.cshtml代碼,如下圖所示:?
@using WebApplication1.Models @model string@{ViewBag.Title = "GetPeople";Layout = "/Views/Shared/_Layout.cshtml";AjaxOptions ajaxOpts = new AjaxOptions { UpdateTargetId = "tableBody" }; }<h2>Get People </h2><table><thead><tr>First</tr><tr>Last</tr><tr>Role</tr></thead><tbody id="tableBody">@Html.Action("GetPeopleData",new { selectedRole = Model})</tbody> </table>@using (Ajax.BeginForm("GetPeopleData", ajaxOpts)) {<div>@Html.DropDownList("selectedRole",new SelectList(new[] { "ALL"}.Concat(Enum.GetNames(typeof(Role)))))<button type="submit">submit</button></div> }MVC框架支持AJax表達的核心在于Ajax.BeginForm 輔助器方法,它可以接受一個AjaxOptions對象作為其參數??梢栽谝晥D開始處,以Razor代碼塊的形式創建AjaxOptions對象,可以在調用Ajax.BeginForm時,內聯的創建他們。?以下是AjaxOptions屬性
| 屬性 | 描述 |
| Confirm | 在形成Ajax請求之前,設置顯示給用戶的確認窗口中的消息 |
| HttpMethod | 設置用來形成請求的HTTP方法——必須是GET或POST |
| InsertionMode | 指定從服務器接受的內容以何種方式插入到HTML。三種選擇被表示成 InsertionMode 枚舉中的值 :InsertAfter、InsertBefore、和Replace(默認值) |
| LoadingElementId | 指定HTML元素的ID,這是執行AJax請求期間要顯示的HTML元素 |
| LoadingElementIdDuration | 指定動畫的持續時間,用于顯露由LoadingElementId指定的元素 |
| UpdateTarget | 設置HTML的ID,從服務器接受的內容將被插入到元素中 |
| Url | 設置所請求服務器的Url |
在上述示例中,將UpdateTargetId屬性設置為tabledata ,當用戶單擊submit提交按鈕時,會形成一個發送給GetPeopleData的動作方法的異步請求,所返回的HTML片段將用于替換tbody元素中的現有內容。運行結果如下圖所示:
確保優雅降級?
使用Ajax的一個問題時,如果用戶禁用JavaScripts(或者使用不支持JavaScript的瀏覽器時),當用戶提交表達時,瀏覽器會放棄當前的HTML頁面,并用目標動作方法返回的片段代替它(于是失去了頁面的主題部分,特別是布局的效果,只剩下了所返回的數據)。如下圖所示:
解決這一個問題的最簡單的辦法是使用AjaxOptions.Url屬性,以便制定異步請求的目標URL作為Ajax.BeginForm方法的參數,而不是以動作名稱作為參數,如下圖所示:
在Ajax請求期間給用戶提供反饋?
使用AJax的一個缺點是用戶觀察不到正在發生的事情,因為,發送給服務器的請求是在后臺形成的。通過使用AJaxOptions.LoadingElementId和AjaxOptions.LoadingElementDuration屬性,可以通知用戶,此刻在執行一個請求。增阿基代碼如下圖所示:
LoadingElementDuration屬性動畫的持續時間,這是向用戶顯露loading元素的時間。這里的值是1000,即1秒,?運行效果如下圖所示:
?請求之前對用戶進行提示
AjaxOptions.Confirm屬性可以用來指定一條消息,用來在每個異步請求之前對用戶進行提示,代碼如下圖所示:
提示框如下圖所示:?
創建Ajax鏈接?
除了表單之外,漸進式Ajax也可以用于創建異步執行的 a 元素。這一機制十分類似于Ajax表單的工作方式。如下圖所示,在GetPeople.cshtml視圖中添加AJax鏈接。
@using WebApplication1.Models @model string@{ViewBag.Title = "GetPeople";Layout = "/Views/Shared/_Layout.cshtml";AjaxOptions ajaxOpts = new AjaxOptions{ UpdateTargetId = "tableBody",Url = Url.Action("GetPeopleData"),LoadingElementId = "loading",LoadingElementDuration = 1000,Confirm = "Do you wish to request new data"}; }<h2>Get People </h2><div id="loading" class="load" style="display:none"><p>Loading Data......</p> </div><table><thead><tr>First</tr><tr>Last</tr><tr>Role</tr></thead><tbody id="tableBody">@Html.Action("GetPeopleData",new { selectedRole = Model})</tbody> </table>@using (Ajax.BeginForm(ajaxOpts)) {<div>@Html.DropDownList("selectedRole",new SelectList(new[] { "ALL"}.Concat(Enum.GetNames(typeof(Role)))))<button type="submit">submit</button></div> }<div>@foreach (string role in Enum.GetNames(typeof(Role))){<div class="ajaxLink"></div>@Ajax.ActionLink(role,"GetPeopleData",new { selectedRole = role},new AjaxOptions { UpdateTargetId = "tableBody"})} </div>此例使用foreach循環,為Role枚舉中定義的每一個值調用了Ajax.ActionLink輔助器,這創建了一組啟用AJax的a元素,這里的a元素具有相同的屬性標簽,如下圖所示:
運行效果如下圖所示:?
點擊鏈接,進行跳轉,結果如下圖所示:?
確保為鏈接優雅降級?
啟用AJax的鏈接與啟用Ajax的表單具有相同的問題。當瀏覽器沒有啟用JavaScript的支持時,單擊這些鏈接只會生成GetPeopleData動作方法的生成的HTML片段。
為了解決這一問題,可以使用AjaxOptions.Url屬性來指定AJax請求的URL,如下圖所示:
<div>@foreach (string role in Enum.GetNames(typeof(Role))){<div class="ajaxLink"></div>@Ajax.ActionLink(role, "GetPeople",new { selectedRole = role }, new AjaxOptions { UpdateTargetId = "tableBody", Url = Url.Action("GetPeopleData", new { selectedRole = role })});} </div>使用AJax回調?
AjaxOption類定義了一組屬性,能夠在Ajax請求生命周期中的各個點上調用JavaScripts函數,如下圖所示:
| 屬性 | JQuery事件 | 描述 |
| OnBegin | beforeSend | 在發送請求之前立即調用 |
| OnComplete | complete | 請求成功時調用 |
| OnFilure | error | 請求失敗時調用 |
| OnSuccess | success | 請求已經完成時調用,不管請求是否成功 |
修改GetPeople.cshtml的代碼如下圖所示:?
@using WebApplication1.Models @model string@{ViewBag.Title = "GetPeople";Layout = "/Views/Shared/_Layout.cshtml";AjaxOptions ajaxOpts = new AjaxOptions{ UpdateTargetId = "tableBody",Url = Url.Action("GetPeopleData"),LoadingElementId = "loading",LoadingElementDuration = 1000,Confirm = "Do you wish to request new data"}; }<h2>Get People </h2><div id="loading" class="load" style="display:none"><p>Loading Data......</p> </div><table><thead><tr>First</tr><tr>Last</tr><tr>Role</tr></thead><tbody id="tableBody">@Html.Action("GetPeopleData",new { selectedRole = Model})</tbody> </table>@using (Ajax.BeginForm(ajaxOpts)) {<div>@Html.DropDownList("selectedRole",new SelectList(new[] { "ALL"}.Concat(Enum.GetNames(typeof(Role)))))<button type="submit">submit</button></div> }<div>@foreach (string role in Enum.GetNames(typeof(Role))){<div class="ajaxLink"></div>@Ajax.ActionLink(role, "GetPeople",new { selectedRole = role }, new AjaxOptions { UpdateTargetId = "tableBody", Url = Url.Action("GetPeopleData", new { selectedRole = role }),OnBegin = "OnBegin",OnFailure = "OnFailure",OnComplete = "OnComplete",OnSuccess = "OnSuccess" });} </div><script type="text/javascript">function OnBegin() {alert("This is the On Begin Callback:")}function OnSuccess(data){alert("This is the OnSuccessCallback:" + data);}function OnFailure(request,error){alert("This is the On Failure Callback:" + error);}function OnComplete(request, status){alert("This is the OnComplete Callback:" + status);} </script>運行后,效果如下:?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?使用JSON
到目前為止的所有示例中,服務器都是渲染HTML片段,并把它們發送給瀏覽器,這是一種完全可接受的技術。但是有點亢長(因為服務器隨數據一起發送了HTML元素),而且它限制了瀏覽器端用這些數據可做的事情。
解決這個問題的一種方式是使用JSON(JavaScrpt Object Notaiton——JavaScripts對象表示法)格式,這是一種與語言無關的數據表示方法。它源自于JavaScript語言,但一直采取自己的生存方式,并被廣泛接受。
修改PeopleController中的代碼,如下圖所示:
public class PeopleController : Controller{private Person[] personData = {new Person { FristName = "Admin",LastName = "FreeMan",Role = Role.Admin},new Person { FristName = "Jacqui",LastName = "Griffyth",Role = Role.User},new Person { FristName = "John",LastName = "Smith",Role = Role.User},new Person { FristName = "Anne",LastName = "Jones",Role = Role.Guset}};// GET: Peoplepublic ActionResult Index(){return View();}private IEnumerable<Person> GetData(string selectedRole){IEnumerable<Person> data = personData;if (selectedRole != "All"){Role selected = (Role)Enum.Parse(typeof(Role), selectedRole);data = personData.Where(p => p.Role == selected);}return data;}public JsonResult GetPeopleDataJason(string selectedRole = "All"){IEnumerable<Person> data = GetData(selectedRole);return Json(data, JsonRequestBehavior.AllowGet);}public PartialViewResult GetPeopleData(string selectedRole = "All"){return PartialView(GetData(selectedRole));}public ActionResult GetPeople(string selectedRole = "All"){return View((object)selectedRole);}}上圖中,新添加了一個動作方法,其名稱為GetPeopleDataJson,它返回一個JsonResult對象,修改GetPeople.cshtml中的代碼,如下圖所示:
@using WebApplication1.Models @model string@{ViewBag.Title = "GetPeople";Layout = "/Views/Shared/_Layout.cshtml";AjaxOptions ajaxOpts = new AjaxOptions{ UpdateTargetId = "tableBody",Url = Url.Action("GetPeopleData"),LoadingElementId = "loading",LoadingElementDuration = 1000,Confirm = "Do you wish to request new data"}; }<h2>Get People </h2><div id="loading" class="load" style="display:none"><p>Loading Data......</p> </div><table><thead><tr>First</tr><tr>Last</tr><tr>Role</tr></thead><tbody id="tableBody">@Html.Action("GetPeopleData",new { selectedRole = Model})</tbody> </table>@using (Ajax.BeginForm(ajaxOpts)) {<div>@Html.DropDownList("selectedRole",new SelectList(new[] { "ALL"}.Concat(Enum.GetNames(typeof(Role)))))<button type="submit">submit</button></div> }<div>@foreach (string role in Enum.GetNames(typeof(Role))){<div class="ajaxLink"></div>@Ajax.ActionLink(role, "GetPeopleData",new { selectedRole = role }, new AjaxOptions { UpdateTargetId = "tableBody", Url = Url.Action("GetPeopleDataJason", new { selectedRole = role }),OnSuccess = "processData" });} </div><script type="text/javascript">function OnBegin() {alert("This is the On Begin Callback:")}function OnSuccess(data){alert("This is the OnSuccessCallback:" + data);}function OnFailure(request,error){alert("This is the On Failure Callback:" + error);}function OnComplete(request, status){alert("This is the OnComplete Callback:" + status);}function processData(data){var target = $("#tableBody");target.empty();for (var i = 0; i < data.length; i++){var person = data[i];target.append("<tr><td>" + person.FirstName + "</td><td>" + person.LastName + "</td><td>" + person.Role + "</td><td>")}} </script>注意:如果返回的數據是非私有的(private),才應該使用JsonRequestBehavior.AllowGet。由于許多web瀏覽器中的安全問題,第三方網站有可能截取響應GET請求返回的JSON數據,這是?JsonResult 默認不響應Get請求的原因。
運行程序,返回的JSON?字符串如下圖所示:
[{"PersonId":0,"FristName":"Jacqui","LastName":"Griffyth","BirthDate":"\/Date(-62135596800000)\/","HomeAddress":null,"Role":1},{"PersonId":0,"FristName":"John","LastName":"Smith","BirthDate":"\/Date(-62135596800000)\/","HomeAddress":null,"Role":1}]這看來有點亂,但是這種處理實際上是相當聰明的——雖然我們不需要包含所有字段。?首先,Person類所定義的全部屬性都標識成了JSON,但是在Person控制器中對一些屬性未賦值。在某些情況下,使用的是某個類型的默認值(例如,IsApproved用的是false值),但是對其他的屬性使用了null(例如HomeAddress),有些值已經被轉換成易于有JavaScript解釋的形式,例如birthDate,而其他的一些則未處理——例如,Role屬性使用了0,而不是Admin.
可以使用如下方式來檢測結果:
可以用如下匿名類的方式來去掉不需要關注的字段:,修改控制器中的代碼:
public JsonResult GetPeopleDataJason(string selectedRole = "All"){var data = GetData(selectedRole).Select(p => new {First = p.FristName,LastName = p.LastName,Role = Enum.GetName(typeof(Role),p.Role)});return Json(data, JsonRequestBehavior.AllowGet);}在動作方法中檢測Ajax請求?
修改People控制器中 GetPeopleData 動作方法的代碼,如下圖所示:
public ActionResult GetPeopleData(string selectedRole = "All"){IEnumerable<Person> data = personData;if (selectedRole != "All"){Role selected = (Role)Enum.Parse(typeof(Role), selectedRole);data = personData.Where(p => p.Role == selected);}if (Request.IsAjaxRequest()){var formattedData = data.Select(p => new{FirstName = p.FristName,LastName = p.LastName,Role = Enum.GetName(typeof(Role), p.Role)});return Json(formattedData, JsonRequestBehavior.AllowGet);}else{return PartialView(data);}}以上代碼中使用Request.IsAjaxRequest 方法對 Ajax請求進行檢測,并且在其結果為true的情況下,交付JSON數據。?
修改GetPerson.cshtml中的代碼如下圖所示:
? ? ? ? ? ?
? ? ? ? ??
以上代碼中, 將所有動作請求都采用了GetPeopleData動作方法,此方法會對是否采用AJax提交進行檢測,從而返回指定的返回值:如果是AJax請求則返回Jason數據,否則返回部分視圖。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的精通ASP.NET MVC ——URL和Ajax辅助器方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Executors源码解读——创建Exe
- 下一篇: Lambda表达式及应用