模型驗證(Model Validation):是確保用戶接收的數據適合於綁定的模型,並且在不合適時,給用戶提供有用的信息,以幫助他們修正其問題的過程。 模型驗證過程一:檢查接收的數據——是保持域模型完整性的方式之一。 模型驗證過程二:幫助用戶修正問題。 示例項目介紹 項目模板:Basic 項目名稱: ...
模型驗證(Model Validation):是確保用戶接收的數據適合於綁定的模型,並且在不合適時,給用戶提供有用的信息,以幫助他們修正其問題的過程。
模型驗證過程一:檢查接收的數據——是保持域模型完整性的方式之一。
模型驗證過程二:幫助用戶修正問題。
示例項目介紹
項目模板:Basic
項目名稱:ModelValidation
一個新的模型類文件:Appointment.cs
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; namespace ModelValidation.Models { public class Appointment { public string ClinetName { get; set; } [DataType(DataType.Date)] public DateTime Date { get; set; } public bool TermsAccepted { get; set; } } }
控制器:Home,用來提供處理Appointment模型類的動作方法
using ModelValidation.Models; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace ModelValidation.Controllers { public class HomeController : Controller { public ViewResult MakeBooking() { return View(new Appointment { Date = DateTime.Now }); } [HttpPost] public ViewResult MakeBooking(Appointment appt) { // 在實際項目中,此處是存儲庫中存儲新的 Appointment 的語句 return View("Completed", appt); } } }
本例不打算實現具體存儲等方面的功能,主要是為了能夠將註意力集中在模型綁定以及驗證過程方面。要清楚驗證模型的主要目的就為了防止在存儲庫中放置劣質或無意義的數據,並避免引起問題。
下麵繼續創建兩個視圖MakeBooking.cshtml、Completed.cshtml:
MakeBooking.cshtml,用來提供一個讓用戶創建新預約的表單:
@model ModelValidation.Models.Appointment @{ ViewBag.Title = "Make A Booking"; } <h4>Book an Appointment</h4> @using (Html.BeginForm()) { <p>Your name: @Html.EditorFor(m => m.ClinetName)</p> <p>Appointment Date: @Html.EditorFor(m => m.Date)</p> <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms $ conditions</p> <input type="submit" value="Make Booking" /> }
用於提交表單後給用戶顯示預約細節的內容的Completed.cshtml視圖:
@model ModelValidation.Models.Appointment @{ ViewBag.Title = "Completed"; } <h4>Your appointment is confirmed</h4> <p>Your name is: @Html.DisplayFor(m => m.ClinetName)</p> <p>The date of your appointment is: @Html.DisplayFor(m => m.Date)</p>
導航地址到/Home/MakeBooking可以看到效果:
按照現在的情況,此刻程式會接收用戶遞交的任何數據,但為了保持程式和域模型的完整性,在接收用戶遞交的Appointment之前,需要確保三件事為“true”。
- 用戶必須提供一個姓名
- 用戶必須提供一個未來的日期(以yyyy/mm/dd格式)
- 用戶必須選擇了覆選框,已接受條款和條件
在這個示例中,模型驗證就是實施這些需求的過程。後面將採不同的技術來實現,以檢查用戶提供的數據,併在輸入無效數據時給予提示。
明確地驗證模型
最直接的驗證模型的方式是在動作方法中進行驗證。下麵是在MakeBooking動作方法的HttpPost版本中對Appointment類所定義的每一個屬性添加明確的檢查:
[HttpPost] public ViewResult MakeBooking(Appointment appt) { if (string.IsNullOrEmpty(appt.ClinetName)) { ModelState.AddModelError("ClinetName", "Please enter your name"); } if (ModelState.IsValidField("Date") && DateTime.Now > appt.Date) { ModelState.AddModelError("Date", "Please enter a date in the future"); } if (!appt.TermsAccepted) { ModelState.AddModelError("TermsAccepted", "You must accept the terms"); } if (ModelState.IsValid) { // 在實際項目中,此處是存儲庫中存儲新的 Appointment 的語句 return View("Completed", appt); } else { return View(); } }
上面對Appointment對象的各個屬性進行檢測時,使用的ModelState.IsValidField方法檢查的是綁定器是否能夠對一個屬性賦值。
註意:如果無法從請求數據中解析到一個值,執行額外檢測或報告其他錯誤消息是沒有意義的。
上面示例中在驗證了模型對象的所有屬性之後,讀取了ModelState.IsValid屬性,以考察是否有錯誤發送,如果在檢查期間調用Model.State.AddModelError方法,或創建Appointment對象時模型綁定器遇到了問題,該方法返回false。
if (ModelState.IsValid) { // 在實際項目中,此處是存儲庫中存儲新的 Appointment 的語句 return View("Completed", appt); } else { return View(); }
如果IsValid值為true,則會得到一個有效的Appointment對象,且會渲染Completed.cshtml視圖;如果是false,則說明出現了問題,此時將掉牙View方法渲染預設視圖。
向用戶顯示驗證錯誤
通過調用View方法來處理驗證錯誤看上去可能有點怪,但是在MakeBooking.cshtml視圖中用來生成input元素的模板視圖輔助器,會檢查視圖模型的驗證錯誤。
如果一個屬性報告了錯誤,那麼輔助器對相應的input元素添加一個CSS的class標簽屬性,其值為input-validation-error。~/Content/site.css文件中包含了一個用於該樣式的預設定義,如:
.input-validation-error {
border: 1px solid #f00;
}
驗證錯誤的效果如圖:
設置覆選框樣式
有些瀏覽器,包括Chrome和Firefox,會忽略運用於覆選框的樣式,這會導致不協調的視覺反饋。解決辦法是在~/Views/Shared/EditorTemplates/Boolean.cshtml中創建一個自定義模板,並將覆選框封裝在一個div元素中。以下是所使用的一個模板,但我們可以將其定製到自己的應用程式中:
@model bool? @if (ViewData.ModelMetadata.IsNullableValueType) { @Html.DropDownListFor(m => m, new SelectList(new[] { "Not Set", "True", "False" }, Model)) } else { ModelState state = ViewData.ModelState[ViewData.ModelMetadata.PropertyName]; bool value = Model ?? false; if (state != null && state.Errors.Count > 0) { <div class="input-validation-error" style="float:left"> @Html.CheckBox("", value) </div> } else { @Html.CheckBox("", value) } }
如果運用這個模板的屬性出現錯誤,該模板會將一個覆選框(CheckBox)封裝在一個運用了input-validation-error樣式的div元素中。
只有當提交的數據能被模型綁定器解析,且能被驗證通過,才會顯示完成視圖,否則,將會提示驗證失敗的視圖。
運用了上面覆選框樣式後的效果:
顯示驗證消息
前面已經實現了高亮顯示錯誤欄位(通過對input元素的CSS樣式設置),但用戶仍不能清除的知道問題何在。對於這一問題,可以使用Html.ValidationSummary輔助器方法顯示已經註冊到該頁面的驗證錯誤的摘要,這也是一種方便而簡單的做法,如果沒有錯誤,該輔助器便不會產生任何HTML,用法如下(粗體部分):
@model ModelValidation.Models.Appointment @{ ViewBag.Title = "Make A Booking"; } <h4>Book an Appointment</h4> @using (Html.BeginForm()) { @Html.ValidationSummary() <p>Your name: @Html.EditorFor(m => m.ClinetName)</p> <p>Appointment Date: @Html.EditorFor(m => m.Date)</p> <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms $ conditions</p> <input type="submit" value="Make Booking" /> }
效果如圖:
對應的顯示這段錯誤消息的HTML如下:
<div class="validation-summary-errors" data-valmsg-summary="true">
<ul>
<li>Please enter your name</li>
<li>Please enter a date in the future</li>
<li>You must accept the terms</li>
</ul>
</div>
ValidationSummary輔助器還有一些重載版本,有一些是允許開發時候設置顯示模型級錯誤的。一些常用的如下所列:
- Html.ValidationSummary():生成所有驗證錯誤的摘要
- Html.ValidationSummary(bool):如果bool參數為true,那麼只顯示模型級錯誤。如果是false則顯示所有錯誤
- Html.ValidationSummary(string):在所有驗證錯誤摘要之前顯示一條消息(消息內容在string參數中)
- Html.ValidationSummary(bool,string):在驗證錯誤消息前顯示一條消息。如果bool參數為true,則只顯示模型級錯誤
當存在由於兩個或多個屬性值之間的相互作用而引發的錯誤時,可以使用模型級錯誤。這裡假設一個場景,作為我們的示例:假設姓名為“Joe”的客戶不能在星期一預約。辦法就是在MakeBooking動作方法中進行明確的驗證檢查,並報告模型級驗證錯誤:
[HttpPost]
public ViewResult MakeBooking(Appointment appt)
{
if (string.IsNullOrEmpty(appt.ClinetName))
{
ModelState.AddModelError("ClinetName", "Please enter your name");
}
if (ModelState.IsValidField("Date") && DateTime.Now > appt.Date)
{
ModelState.AddModelError("Date", "Please enter a date in the future");
}
if (!appt.TermsAccepted)
{
ModelState.AddModelError("TermsAccepted", "You must accept the terms");
}
if (ModelState.IsValidField("ClientName")
&& ModelState.IsValidField("Date")
&& appt.ClinetName == "Joe"
&& appt.Date.DayOfWeek == DayOfWeek.Monday)
{
ModelState.AddModelError("","Joe cannot book appointment on Mondays");
}
if (ModelState.IsValid)
{
// 在實際項目中,此處是存儲庫中存儲新的 Appointment 的語句
return View("Completed", appt);
}
else
{
return View();
}
}
示例的粗體部分的ModelState.AddModelError方法的第一個參數給出的是空字元串(""),這樣就可以註冊一條模型級錯誤。後面修改一下MakeBooking.cshtml視圖,使其只顯示模型機錯誤,這樣方便我們看到最終的效果:
@model ModelValidation.Models.Appointment @{ ViewBag.Title = "Make A Booking"; } <h4>Book an Appointment</h4> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <p>Your name: @Html.EditorFor(m => m.ClinetName)</p> <p>Appointment Date: @Html.EditorFor(m => m.Date)</p> <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms $ conditions</p> <input type="submit" value="Make Booking" /> }
這樣已修改,從上圖的效果中可以看出出現了兩個錯誤。一個是模型級的錯誤,就是紅色錯誤消息部分;第二個是是沒有勾選覆選框導致的,但是現在不會再顯示問題的描述消息了。
顯示屬性級驗證消息
由於屬性級驗證消息是可以顯示在相應欄位的旁邊的,所以,就很容易把模型級錯誤和屬性級錯誤分開,將模型級錯誤作為整體的摘要顯示了,這樣也更合理一些。
要想單獨在相應的欄位旁邊顯示屬性級驗證消息,一般可以通過使用Html.ValidationMessageFor輔助器方法實現。
下麵是再次對MakeBooking.cshtml視圖的調整:
使用其他驗證技術
在模型綁定器中執行驗證
驗證也是預設模型綁定器的綁定過程的一部分。模型綁定器會為模型對象中的每一個屬性執行一些基本的驗證。
下圖展示瞭如果為示例項目中模型對象的Date屬性遞交空或錯誤內容的驗證結果,這都是預設模型綁定器實現的:
預設的模型綁定器類DefaultModelBinder提供了一些可以重寫的方法,以使得開發時可以方便的為一個綁定器添加驗證。通過重寫這些方法,便可以在創建自定義模型綁定器時,將自己的驗證邏輯放在綁定過程之中。
建議:對於使用自定義驗證邏輯時,建議在模型類運用元數據的方式處理驗證,而不在模型綁定器中添加自定義驗證邏輯。
DefaultModelBinder中對綁定過程添加驗證的方法:
方法 |
描述 |
預設實現 |
OnModelUpdated |
在綁定器試圖對模型對象中的所有屬性進行賦值時調用 |
運用由模型元數據定義的驗證規則,並用ModelState註冊錯誤。 |
SetProperty |
在綁定器對一個特定屬性運用一個值時調用 |
如果該屬性不能保存null值,並且沒有可以運用的值,那麼,將用ModelState註冊一條“The<name> field is required (<name>欄位是必需的)”的錯誤消息(如上圖中顯示的“Date 欄位是必需的”)。如果有一個值,但不能進行解析,將註冊“The value <> is not valid for <name>(值”<value>”對 <name> 無效)”的錯誤 |
用元數據指定驗證規則
使用元數據指定規則的一個優點是,在整個項目中運用綁定過程的任何地方,都會強制執行驗證規則,而不只存在於個別動作方法之中。
拿Appointment作為示例,看看是如何實現這種驗證方式的:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; namespace ModelValidation.Models { public class Appointment { [Required] public string ClinetName { get; set; } [DataType(DataType.Date)] [Required(ErrorMessage = "Please enter a date")] public DateTime Date { get; set; } [Range(typeof(bool), "true", "true", ErrorMessage = "You must accept the terms")] public bool TermsAccepted { get; set; } } }
上面代碼中運用了兩個驗證註解屬性——Required和Range。下麵給出了內建驗證註解屬性的一些介紹:
- Compare:
示例:[Compare("MyOtherProperty")]
描述:兩個屬性必須有同樣的值。當要求用戶對一個屬性提供兩次同樣的值時,這是有用的。如:一個郵件地址或一個口令
- Range:
示例:[Range (10,20)]
描述:一個數字值(或實現IComparable的任何屬性類型),必須不超出指定的最小值和最大值。為了指定只有一端的邊界,可以用一個MinValue或MaxValue常數。如:[Range (int.MinValue,50)]
- RegularExpression:
示例:[RegularExpression ("pattern")]
描述:一個字元串值,必須匹配指定的正則表達式模式。註意,該模式必須匹配用戶提供的所有值,而不只是其中的一個子串。預設它是大小寫敏感的,但可以通過運用(?!)修飾符,使大小寫不敏感。如:[RegularExpression ("(?!)mypattern")]
- Required:
示例:[Required]
描述:必須是一個非空值,或一個不是只含空格的字元串。如果希望空格作為可接受值,可以用[Required(AllowEmptyStrings = true)]
- StringLength:
示例:[StringLength (10)]
描述:一個字元串,必須不超過指定的最大長度。也可以指定一個最小長度:[StringLength (10,MinimumLength=2)]
所有上述這些驗證註解屬性都可以給ErrorMessage屬性設置一個值,以指定一個自定義的錯誤消息,如:
[Required(ErrorMessage = "Please enter a date")]
內建的驗證註解屬性是很基本的,且只能做屬性級驗證。即使這樣,仍需要一些技巧。可以考慮下麵這個例子:
[Range(typeof(bool), "true", "true", ErrorMessage = "You must accept the terms")]
如果希望用戶選中了覆選框,以接受條款。此時不能使用Required註解屬性,因為用於布爾值的模板輔助器會產生一個隱藏的HTML元素,以確保即使覆選框未選中也會獲得一個值。為瞭解決這一問題,可以利用Range註解屬性的一個特性,它讓我們提供一個類型,並以字元串值指定上下限。通過把上下限均設置為true,可以為使用覆選框的布爾屬性的編輯器視圖創建一個等效的Required註解屬性。
註意:DataType註解屬性不能用於驗證用戶輸入——只能對使用模板輔助器進行渲染的值提供提示。因此,不要指望用DataType(DataType.EmailAddress)這樣的註解屬性來強制一個特定的格式。
1.創建自定義的屬性驗證註解屬性
使用Range註解屬性類固然能夠變相的實現Required註解屬性的功能,但這還是顯得有些笨拙。好在,可以創建自定義的屬性驗證註解屬性,而這隻需要從ValidationAttribute類進行派生即可。
為了演示其工作機制,需要向項目中添加一個Infrastructure文件夾,併在其中創建一個MustBeTrueAttribute類:
using System.ComponentModel.DataAnnotations; namespace ModelValidation.Infrastructure { public class MustBeTrueAttribute : ValidationAttribute { public override bool IsValid(object value) { return value is bool && (bool)value; } } }
在這個示例中,驗證邏輯很簡單。如果這是一個具有true值的bool型,那麼這個值就是有效的。下麵看看如何使用這個自定義的註解屬性,以實現前面的Range註解屬性的驗證效果:
using ModelValidation.Infrastructure; using System; using System.ComponentModel.DataAnnotations; namespace ModelValidation.Models { public class Appointment { [Required] public string ClinetName { get; set; } [DataType(DataType.Date)] [Required(ErrorMessage = "Please enter a date")] public DateTime Date { get; set; } [MustBeTrueAttribute(ErrorMessage = "You must accept the terms")] public bool TermsAccepted { get; set; } } }
這樣看起來會更簡便,下圖是最終效果:
還有一種方式是,通過內建的驗證註解屬性來派生新類,這等於提供了擴展內建驗證註解屬性行為的能力。下麵是Infrastructure中的示例類FutureDateAttribute:
using System; using System.ComponentModel.DataAnnotations; namespace ModelValidation.Infrastructure { public class FutureDateAttribute : RequiredAttribute { public override bool IsValid(object value) { return base.IsValid(value) && ((DateTime)value) > DateTime.Now; } } }
在代碼中,由於使用的基類的IsValid方法,所以重寫方法包含了所有基本驗證步驟,下麵是對其應用:
using ModelValidation.Infrastructure; using System; using System.ComponentModel.DataAnnotations; namespace ModelValidation.Models { public class Appointment { [Required] public string ClinetName { get; set; } [DataType(DataType.Date)] [FutureDateAttribute(ErrorMessage = "Please enter a date in the future")] public DateTime Date { get; set; } [MustBeTrueAttribute(ErrorMessage = "You must accept the terms")] public bool TermsAccepted { get; set; } } }
2.創建模型驗證註解屬性
現在來看看自定義的模型驗證註解屬性,為了演示,在Infrastructure文件夾中創建NoJoeOnMondaysAttribute.cs類:
using System; using System.ComponentModel.DataAnnotations; using ModelValidation.Models; namespace ModelValidation.Infrastructure { public class NoJoeOnMondaysAttribute : ValidationAttribute { public NoJoeOnMondaysAttribute() { ErrorMessage = "Joe cannot book appointment on Mondays"; } public override bool IsValid(object value) { Appointment app = value as Appointment; if (app == null || string.IsNullOrEmpty(app.ClinetName) || app.Date == null) { // 還沒有正確的類型的模型要驗證,或者還沒有所需要的 ClientName 和 Date 屬性的值 return true; } else { return !(app.ClinetName == "Joe" && app.Date.DayOfWeek == DayOfWeek.Monday); } } } }
與單個屬性的情況相比,在模型類上使用的驗證註解屬性時,模型綁定器傳遞給IsValid方法的object參數將是模型對象,比如Appointment。
對於上述代碼,該驗證註解屬性會進行檢查,以確保已經有了一個Appointment對象,且如果有,則也就有了可以使用的ClientName和Date屬性的值。如果是這樣,就可以確保Joe不能試圖預約星期一。下麵給出了在Appointment類上的使用情況:
using ModelValidation.Infrastructure; using System; using System.ComponentModel.DataAnnotations; namespace ModelValidation.Models { [NoJoeOnMondays] public class Appointment { [Required] public string ClinetName { get; set; } [DataType(DataType.Date)] [FutureDateAttribute(ErrorMessage = "Please enter a date in the future")] public DateTime Date { get; set; } [MustBeTrueAttribute(ErrorMessage = "You must accept the terms")] public bool TermsAccepted { get; set; } } }
此時,在動作方法中執行著兩個同一種類的驗證,而且都使用驗證註解屬性,這也意味著,對同一驗證問題,用戶會看到兩個類似的錯誤消息。為瞭解決這一問題,已經從Home控制器的MakeBooking動作方法中刪除了明確的驗證檢查,效果是讓驗證註解屬性全權負責執行自定義的驗證檢查:
using ModelValidation.Models; using System; using System.Web.Mvc; namespace ModelValidation.Controllers { public class HomeController : Controller { public ViewResult MakeBooking() { return View(new Appointment { Date = DateTime.Now }); } [HttpPost] public ViewResult MakeBooking(Appointment appt) { if (ModelState.IsValid) { // 在實際項目中,此處是存儲庫中存儲新的 Appointment 的語句 return View("Completed", appt); } else { return View(); } } } }
需要瞭解當檢查到屬性級問題時,模型級驗證註解屬性不會生效。此時,啟動程式,並輸入姓名Joe和日期2016/5/30,並取消覆選框的選中狀態,當遞交表單時,會看到只有關於覆選框的警告。然後選中覆選框,然後再次遞交,只有這時才會看到模型級錯誤:
定義自驗證模型
通過繼承IValidatableObject介面,便可以將驗證邏輯在模型中實現,者便可得到自驗證模型:
using ModelValidation.Infrastructure; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ModelValidation.Models { public class Appointment : IValidatableObject { public string ClinetName { get; set; } [DataType(DataType.Date)] public DateTime Date { get; set; } public bool TermsAccepted { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { List<ValidationResult> errors = new List<ValidationResult>(); if (string.IsNullOrEmpty(ClinetName)) { errors.Add(new ValidationResult("Please enter your name")); } if (DateTime.Now > Date) { errors.Add(new ValidationResult("Please enter a date in the future")); } if (errors.Count == 0 && ClinetName == "Joe") { errors.Add(new ValidationResult("Joe cannot book appointment on Mondays")); } if (!TermsAccepted) { errors.Add(new ValidationResult("You must accept the terms")); } return errors; } } }
IValidatableObject介面定義的Validate方法以ValidationContext為參數,這個參數不是MVC專用的,且也不常用。Validate方法的返回結果是ValidationResult對象的一個枚舉,其中每一個都表示一個驗證錯誤。
如果模型類實現了IValidatableObject介面,那麼在模型綁定器對每一個模型屬性賦值之後,便會調用該Validate方法。這樣做的好處是,它結合了將驗證邏輯放入動作方法的靈活性,但又具有任何時候都會運用模型綁定過程創建模型類型實例的一致性。另一個好處是,可以在一個地方將模型級和屬性級驗證結合在一起,這意味著所有錯誤都顯示在一起。很多人不習慣這麼做,但這也是十分適合MVC的設計模式的,且這樣擁有了靈活性和一致性。
執行客戶端驗證
之前講的驗證技術都是伺服器端驗證。很多情況下需要得到即時的反饋,這就需要客戶端驗證了。客戶端驗證通常是使用JavaScript實現的,由於這種驗證是在客戶端進行的,也就是說在數據傳遞到服務端之前,這樣就給用戶提供了即時反饋並修正錯誤的機會。
MVC框架支持漸進式客戶端驗證(Unobtrusive Client-Side Validation)。漸進式指的是在生成的HTML元素上添加驗證標簽屬性來表示驗證規則。這些標簽屬性由包含在MVC框架中的JavaScript庫進行解釋,框架又轉而依靠對jQUery驗證庫所作的配置,並完成實際的驗證工作。
提示:客戶端驗證致力於驗證個別屬性。並且如果使用MVC內建的支持,則很難建立模型級的客戶端驗證。
客戶端驗證機制可參見下圖:
啟用和禁用客戶端驗證
客戶端驗證是由Web.config文件中的兩個設置來控制的。如:
為了是客戶端驗證生效,紅框中的這兩個屬性必須設置為true。預設創建MVC項目時這些會自動創建,並被設置為true。
提示:也可以配置基於個別視圖的客戶端驗證,只需要在視圖的一個razor代碼塊中設置HtmlHelper.ClientValidationEnabled和HtmlHelper.UnobtrusiveJabaScriptEnabled即可。
在上面配置正確後,還需要確保在發送給瀏覽器的HTML中引用了以下這三個JavaScript庫才行:
- /Scripts/jquery-1.8.2.min.js:jQuery主庫
- /Scripts/jquery.validate.min.js:jQuery驗證
- /Scripts/jquery.validate.unobtrusive.min.js:jQuery漸進式驗證
給一個視圖添加這些JavaScript文件最簡單的方式是使用新的腳本捆綁包特性(其工作原理在相關專項中再做介紹)。這是MVC4中新增特性。下麵是一個示例,我們修改的是項目的佈局視圖:_Layout.cshtml:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <body> @RenderBody() @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/jqueryval") @RenderSection("scripts", required: false) </body> </html>
使用客戶端驗證
下麵刪除了Appointment模型類中IValidatableObject介面的實現,因為它對客戶端驗證無效,另外,還做了其他修改,以使其支持客戶端驗證:
using ModelValidation.Infrastructure; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ModelValidation.Models { public class Appointment { [Required] [StringLength(10, MinimumLength = 3)] public string ClinetName { get; set; } [DataType(DataType.Date)] public DateTime Date { get; set; } public bool TermsAccepted { get; set; } } }
下圖是在”Your name”文本框中輸入X後,使用Tab鍵或點擊其他輸入元素後即可顯示出來的效果:
驗證結果之所以即時生效,實際上是因為執行驗證的JavaScript代碼會阻止遞交表單,直到不再出現為止(如果用戶繼續輸入正確的內容,錯誤消息將會即時消失)。
理解客戶端驗證機制
使用MVC框架客戶端驗證特性的優點之一是不必編寫JavaScript,而是用HTML標簽屬性來表示驗證規則。下麵對比一下啟用和禁用客戶端驗證時所得到的HTML,下例是以ClientName屬性,通過Html.EditorFor輔助器所渲染的結果:
禁用:
<input name="ClinetName" class="text-box single-line" id="ClinetName" type="text" value="">
啟用:
<input name="ClinetName" class="text-box single-line" id="ClinetName" type="text" value="" data-val-required="ClinetName 欄位是必需的。" data-val-length-min="3" data-val-length-max="10" data-val-length="欄位 ClinetName 必須是一個字元串,其最小長度為 3,最大長度為 10。" data-val="true">
上面啟用時被標黃色背景的為與客戶端驗證相關的項。
MVC的客戶端驗證支持不會生成任何JavaScript腳本或JSON數據來指導驗證過程,這就像框架的其他部分,依靠的是約定:添加的標簽屬性以data-val為首碼,jQuery驗證庫通過檢查這個標簽屬性,會識別出這是一個需要驗證的欄位。
各個驗證規則是以一個data-val-<name>標簽屬性的形式指定的,其中name是所運用的規則。如:運用於模型類的Required註解屬性,生成的是data-val-required標簽屬性。有些規則需要額外的標簽屬性,如長度規則:data-val-length-min和data-val-length-max標簽屬性,用例指定最小和最大字元串長度。
MVC客戶端驗證的一個很好的特點就是,不管是客戶端還是伺服器端驗證,用以指定驗證規則的屬性是相同的。也就是說,從不支持JavaScript的瀏覽器而來的數據會得到通用的驗證——將轉由伺服器端進行驗證,而無需做額外的任何工作。漸進式也就是指的這一點:當jQuery驗證庫不能正常發揮作用時,這些標簽屬性在客戶端將是一些正常的HTML標記(但不起作用),此時客戶端驗證失效,而驗證則會在伺服器端進行。
MVC客戶端驗證與jQuery驗證
MVC客戶端驗證:建立在jQuery驗證庫之上,藉助於驗證標簽屬性來使用jQuery驗證庫。無需編寫腳本。
jQuery驗證:通過Jav