ASP.NET MVC 框架中包含一組 Ajax 輔助方法,能夠用來建立表單和指向控制器操做的連接,它們是異步的,且不用編寫任何腳本代碼來實現程序的異步性,但須要引入腳本文件 jquery.unobtrusive-ajax.js,MVC 4 應用程序默認在 _Layout 視圖中包含這個腳本:html
固然,也能夠去除它,而在須要的頁面上手動引入:jquery
在 Razor 視圖中,可經過 Ajax 屬性方法,該方法可建立一個具備異步行爲的錨標籤:web
<div id="dailydeal">
<!--
參數1:連接文本
參數2:要異步調用的操做名稱
參數3:AjaxOptions 對象
-->
@Ajax.ActionLink("Click here to see today's special!",
"DailyDeal",
new AjaxOptions
{
UpdateTargetId = "dailydeal",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET"
}
)
</div>
AjaxOptions 參數指定了發送請求和處理服務器返回的結果的方式,該參數還包括處理錯誤、顯示和加載元素、顯示確認對話框等的選項:ajax
服務器端須要有一些響應的代碼,此例中爲 DailyDeal 操做,這裏僅返回一個簡單的系統時間字符串:json
public string DailyDeal()
{
return DateTime.Now.ToString();
}
Ajax.ActionLink 生成的內容可以獲取服務器的響應,並能夠直接把新內容移植到頁面中,這是如何發生的呢?數組
若是查看該方法渲染的標記,就會看到以下代碼:瀏覽器
<a data-ajax="true"
data-ajax-method="GET"
data-ajax-mode="replace"
data-ajax-update="#dailydeal"
href="/Home/DailyDeal">
Click here to see today's special!
</a>
仔細看,ActionLink 方法中指定的全部設置都被編碼成了 HTML 特性,而且大多數特性都有 data-前綴,一般稱爲 data-特性。服務器
HTML 5 規範爲私有應用程序保留了 data-特性,Web 瀏覽器不會嘗試解釋它的內容,所以能夠放心的把數據交給它,這些數據不會影響頁面的顯示或渲染。添加 jquery.unobtrusive-ajax.js 文件的目的是查找特定的 data-特性,而後操縱元素使其表現出不一樣行爲。本例中,jQuery 查找了 a[data-ajax]=true 的全部錨標記,腳本識別了該異步元素,它天然就能夠讀取該元素的其餘設置(像替換模式、更新目標、HTTP 方法),還能經過使用 jQuery 鏈接事件和發送請求來修改該元素的行爲。app
若是要在頁面增長一個搜索功能,由於須要由用戶的輸入而作出相應的反饋,因此必須在頁面上放置一個 form 表單,這裏放一個異步表單:框架
@using (Ajax.BeginForm("ArtistSearch", "Home",
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
OnFailure = "searchFailed",
LoadingElementId = "ajax-loader",
UpdateTargetId = "searchResults"
}
))
{
<input type="text" name="q" />
<input type="submit" value="search" />
<img id="ajax-loader" src="@~Content/Images/ajax-loader.gif" style="display:none" />
}
當用戶進行搜索時,瀏覽器會向 Home 控制器的 ArtistSearch 操做發送異步 GET 請求; 當執行異步請求時,客戶端框架會顯示 LoadingElementId 指定的元素,一般這個元素會出現一個具備動畫效果的圖片來告知用戶後臺正在進行一些處理;若是服務器代碼返回一個錯誤,就意味着 Ajax 輔助方法執行失敗,OnFailure 選項會觸發預先設置的 js 函數,至少能夠提示一個錯誤信息,讓用戶知道咱們已經盡力了。
function searchFailed() {
$("#searchResults").html("Sorry, there was a problem with the search.");
}
對於數據註解特性來講,ASP.NET MVC 框架的客戶端驗證是默認開啓的。下面介紹 Album 類的 Title 和 Price 屬性:
[Required(ErrorMessage = "An Album Title is required")]
[StringLength(160)]
public string Title { get; set; }
[Required(ErrorMessage = "Price is required")]
[Range(0.01, 100.00, ErrorMessage = "Price must be between 0.01 and 100.00")]
public decimal Price { get; set; }
ASP.NET MVC 模型綁定器在設置這些屬性時會執行服務器端驗證,但同時,這些內置的特性也會觸發客戶端驗證,客戶端驗證依賴於 jQuery 驗證插件。
默認狀況下,非侵入式 JavaScript 和客戶端驗證在 ASP.NET MVC 中是啓用的,但經過 web.config 文件中的設置也能夠改變這些行爲:
若是須要實現客戶端驗證,那麼須要一對腳本標籤:
第一個 script 是驗證插件,jQuery 驗證明現了掛接到事件須要的全部邏輯(像提交和焦點事件),此外,還要執行客戶端驗證規則,該插件提供了豐富的默認驗證規則集。
第二個 script 用於 jQuery 驗證的 Microsoft 非侵入式適配器。這段腳本中的代碼用來獲取 ASP.NET MVC 框架發出的元數據,並將這些元數據轉換成 jQuery 驗證可以理解的數據!
那麼,這些元數據從何而來?先看下面的 view 片斷:
<p>
@Html.LabelFor(model => model.Title)
@Html.TextBoxFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</p>
<p>
@Html.LabelFor(model => model.Price)
@Html.TextBoxFor(model => model.Price)
@Html.ValidationMessageFor(model => model.Price)
</p>
這裏,輔助方法 TextBoxFor 是關鍵,它爲基於元數據的模型構建輸入元素,當它看到驗證元數據(屬性上的數據註解)時,會將這些元數據放入到渲染的 HTML 中:
<p>
<label for="Title">Title</label>
<input data-val="true" data-val-length="字段 Title 必須是最大長度爲 160 的字符串。" data-val-length-max="160" data-val-required="An Album Title is required" id="Title" name="Title" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
</p>
<p>
<label for="Price">Price</label>
<input data-val="true" data-val-number="字段 Price 必須是一個數字。" data-val-range="Price must be between 0.01 and 100.00" data-val-range-max="100" data-val-range-min="0.01" data-val-required="Price is required" id="Price" name="Price" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
</p>
再次看到了 data-特性,上述代碼中,jquery.validate.unobtrusive 腳本負責使用這個元數據(以 data-val="true"開頭)查找元素,並結合 jQuery 驗證插件來執行元數據內的驗證規則!jQuery 驗證可運行每一個擊鍵和焦點事件上的規則,給用戶提供關於錯誤值的及時反饋,當出現錯誤時,驗證插件也能阻止表單提交,這也意味着沒必要在服務器上處理註定要失敗的請求。
以前的篇章中,曾經寫過自定義驗證特性 MaxWordsAttribute 來驗證一個字符串中的單詞數量:
public class MaxWordsAttribute : ValidationAttribute
{
private readonly int _maxWords;
public MaxWordsAttribute(int maxWords) : base("Too many words in {0}")
{
this._maxWords = maxWords;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
var valueString = value.ToString();
if (valueString.Split(' ').Length > this._maxWords)
{
return new ValidationResult("Too many words!");
}
}
return ValidationResult.Success;
}
}
// 能夠啓用這個自定義特性
[Required(ErrorMessage = "An Album Title is required")]
[StringLength(160)]
[OAuthMVC.Filters.MaxWords(10)]
public string Title { get; set; }
1. 如今的問題是,這個自定義特性只支持服務器端的驗證,而爲了支持客戶端驗證,須要讓特性實現接口 System.Web.Mvc.IClientValidatable,IClientValidatable 接口定義了單個方法 GetClientValidationRules,當 ASP.NET MVC 框架使用這個接口查找驗證對象時,它會調用 GetClientValidationRules 方法來檢索 ModelClientValidationRule 對象序列,這些對象攜帶有框架發送給客戶端的元數據和規則,實現以下:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationParameters.Add("wordcount", _maxWords);
rule.ValidationType = "maxwords";
yield return rule;
}
要實如今客戶端執行驗證,須要提供:驗證失敗時的提示消息、容許的單詞數的範圍、一段用來計算單詞數量的 JavaScript 代碼標識。這些信息就是代碼放進返回規則中的內容(如需在客戶端觸發多種類型的驗證,代碼能夠返回多個規則)。ASP.NET MVC 框架在客戶端上將這些返回的規則序列化爲 data-特性:
<input data-val="true"
data-val-length="字段 Title 必須是最大長度爲 160 的字符串。"
data-val-length-max="160"
data-val-maxwords="Too many words in Title"
data-val-maxwords-wordcount="10"
data-val-required="An Album Title is required" id="Title" name="Title" type="text" value="" />
驗證類型(rule.ValidationType)和全部驗證參數的名稱(rule.ValidationParameters)必須都是小寫,由於它們的值必須可以做爲合法的 HTML 特性標識符使用。
2. 先前說過,客戶端也須要一段計算單詞數量的 JS 代碼標識。幸運的是,咱們不必在客戶端編寫代碼來從 data-特性 中挖掘元數據值。爲了執行驗證工做,須要如下兩段腳本代碼:
這兩段代碼都在同一個文件中,假設是 MusicScripts.js 文件,要確保 MusicScripts.js 出如今驗證腳本以後:
首先要編寫的代碼是適配器。MVC 框架的非侵入式驗證擴展存儲了 jQuery.validator.unobtrusive.adapters 對象中的全部適配器,這些適配器對象公開了一個 API,能夠用來添加新的適配器,以下表:
名 稱 |
描 述 |
addBool | 爲「啓用」或「禁止」的驗證規則建立適配器,不須要額外參數。 |
addSingleVal | 爲須要從元數據中檢索惟一參數值的驗證規則建立適配器。 |
addMinMax | 建立一個映射到驗證規則集的適配器,一個檢查最小值,一個檢查最大值,且這兩個規則中至少有一個要依靠獲得的數據運行。 |
Add | 建立一個不適合前面類別的適配器,由於它須要額外參數或額外的設置代碼 |
對於最大單詞數的情形,可以使用 addSingleVal 或 addMinMax (或 Add,由於它適用於任何場合),因爲不須要檢查單詞的最小數量,所以,選擇第一個 addSingleVal:
// 參數一:適配器名稱,必須與服務器端設置的 ValidationType 值匹配
// 參數二:要從元數據中檢索的參數的名稱,它匹配服務器的 ValidationParameters 集合的參數名稱
$.validator.unobtrusive.adapters.addSingleVal("maxwords", "wordcount");
適配器相對而言比較簡單,主要目標是識別非侵入式擴展要定位的元數據。有了適配器,如今就能夠編寫驗證器。全部驗證器都在 jQuery.validator 對象中,與 adapters 對象相似,validator 對象也有一個 API 函數,可用來添加新驗證器,該函數的名稱是:addMethod:
// 參數一:驗證器名稱,默認狀況下,要匹配適配器的名稱,而適配器的名稱又要匹配服務器端的 ValidationType 值
// 參數二:當驗證發生時被調用
// value:用戶輸入的值,如專輯的名稱
// element:輸入元素,其中也包含了要驗證的值(在 value 自己沒有提供足夠信息的狀況下使用)
// 第三個函數參數:一個全部驗證參數的數組,這個示例中包含了單一驗證參數(即最大的單詞數量)
$.validator.addMethod("maxwords", function (value, element, maxwords) {
if (value) {
if (value.split(' ').length > maxwords) {
return false;
}
}
return true;
});
默認狀況下,ASP.NET MVC 框架不容許使用 JSON 負載響應 HTTP GET 請求。若是爲了響應 GET 請求,須要發送 JSON 格式的數據,就須要使用 JsonRequestBehavior.AllowGet 做爲 Json 方法的第二個參數顯式的來支持這一操做,例如:
public ActionResult DailyDeal()
{
var album = new Models.Album();
return Json(album, JsonRequestBehavior.AllowGet);
}
然而,這樣就給了惡意用戶可乘之機,他們能夠經過知名的 JSON 劫持進程來得到對 JSON 負載的訪問權,所以,不要在 GET 請求中使用 JSON 格式返回敏感信息。請參看這個例子:http://haacked.com/archive/2009/06/25/json-hijacking.aspx
儘管 Ajax 輔助方法提供了大量功能,但如今咱們刪除這些輔助方法,從頭開始。jQuery 提供了從服務器檢索數據的各類 API,首先修改表單使其直接使用 jQuery 而不使用 Ajax 輔助方法,修改後的 Index.cshtml 視圖以下:
<form id="artistSearch" method="get" action="@Url.Action("ArtistSearch","Home")">
<input type="text" name="q" data-autocomplate-source="@Url.Action("QuickSearch","Home")" />
<input type="submit" value="search" />
<img id="ajax-loader" src="@~Content/Images/ajax-loader.gif" style="display:none" />
</form>
顯而易見,若是不使用 Ajax 輔助方法,咱們就須要本身編寫 JS 代碼來向服務器請求 HTML:
$("#artistSearch").submit(function (event) {
// 阻止觸發默認事件,這裏能夠阻止表單直接提交到服務器
event.preventDefault();
var form = $(this);
$.getJSON(form.attr("action"), form.serialize(), function (data) {
// data 是服務器返回的 JSON 數據,這裏能夠作任何事,包括渲染客戶端模板(如 Mustache 等)
});
});
form.attr("action") 可使用 action 的特性值獲得正確的 URL, form.serialize() 將表單內部全部輸入值鏈接成一個字符串來構建數據,這裏的例子是「q=xxx」,發出一個請求後,將獲得的 JSON 響應反序列化爲一個對象,而後做爲參數來調用一個回調函數,回調函數內部能夠作你想作的事。
當要實現對 Ajax 請求的徹底控制時,能夠使用 jQuery.ajax 方法。該方法只使用一個參數,但能夠指定 HTTP 動詞(如GET、POST、DELETE、PUT等)、超時、錯誤處理程序等。全部其它的異步通訊方法最終都調用了 ajax 方法!如:
$("#artistSearch").submit(function (event) {
// 阻止觸發默認事件,這裏能夠阻止表單直接提交到服務器
event.preventDefault();
var form = $(this);
$.ajax({
url: form.attr("action"),
data: form.serialize(),
beforeSend: function () {
$("#ajax-loader").show();
},
complete: function () {
$("#ajax-loader").hide();
},
error: searchResults,
success: function (data) {
var html = Mustache.to_html($("#artistTemplate").html(), { artist: data });
$("#searchResults").empty().append(html);
}
});
});
調用 ajax 方法是繁瑣的,由於須要自定義不少設置。url 和 data 屬性就像是傳遞給 load 和 getJSON 方法的參數;回調期間又分別顯示和隱藏了 gif 動畫;即使調用服務器致使失敗,jQuery 都將調用 complete 指定的回調函數;error 和 success 中只會有一個能夠調用成功。