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 中只会有一个能够调用成功。