精通ASP.NET MVC ——模型验证
文章內(nèi)容較長,用于記錄自己學習。模型驗證(Model Vaildation)是確保應(yīng)用程序所接受的數(shù)據(jù)適合于綁定到模型,并且在不合適時給用戶提供有用的信息,以幫助他們修正問題的過程。
準備示例項目?
新建一個空的MVC項目,名叫ModelVaildation。
在Model文件夾中,新建一個Appointment.cs的文件,如下圖所示:
public class Appointment{public string ClientName { get; set; }[DataType(DataType.Date)]public DateTime Date { get; set; }public bool TermsAccepted { get; set; }}創(chuàng)建一個Home控制器,代碼如下圖所示:
public class HomeController : Controller{// GET: Homepublic ViewResult MakeBooking(){return View(new Appointment { Date = DateTime.Now});}[HttpPost]public ViewResult MakeBooking(Appointment appt){//在實際項目中,此處是在數(shù)據(jù)庫中存儲新的Appointment對象return View("Completed", appt);}}創(chuàng)建布局頁_Layout.cshtml,代碼如下圖所示:
<!DOCTYPE html><html> <head><meta charset="utf-8"/><meta name="viewport" content="width=device-width" /><title>@ViewBag.Title</title><style type="text/css">.field-validation-error {color:#f00;}.validation-summary-errors {color:#f00; font-weight:bold;}.input-validation-error {border:2px solid #f00; background-color :#fee;}input[type="checkbox"].input-validation-error {outline:2px solid #f00;}</style> </head> <body><div>@RenderBody()</div> </body> </html>創(chuàng)建一個視圖啟動文件_ViewStart.cshtml,以便布局頁運用于視圖,如下圖所示:
@{ Layout = "~/Views/Shared/_Layout.cshtml"; }創(chuàng)建MakeBooking.cshtml視圖,代碼如下圖所示:?
@model ModelVaildation.Models.Appointment <!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>MakeBooking</title> </head> <body> <h4>Book an Appointment</h4>@using (Html.BeginForm()) {<p>Your Name : @Html.EditorFor(m => m.ClientName)</p><p>Appointment Date: @Html.EditorFor(m => m.Date)</p><p>@Html.EditorFor(m => m.TermsAccepted)</p><input type="submit" value="Make Booking"/>} </body> </html>創(chuàng)建Completed.cshtml文件,代碼如如下圖所示:?
<html> <head><meta name="viewport" content="width=device-width" /><title>Completed</title> </head> <body> <h4> Your Name is confirmed</h4><p> Your Name :<b>@Html.EditorFor(m => m.ClientName)</b></p><p> The data of your appointment is :<b>@Html.DisplayFor(m => m.Date)</b></p> </body> </html>運行程序并導(dǎo)航到/Home/MakeBooking?,輸入文本數(shù)據(jù),如下圖所示:
? ? ? ? ? ??
點擊按鈕,結(jié)果如下:?
? ? ? ? ? ? ?
現(xiàn)在,我們需要增加三個驗證來確定用戶提交的時候是可接受的數(shù)據(jù):?
1、用戶必須提供一個姓名。
2、用戶必須提供一個未來的可用的日期(以mm/dd/yyyy格式)。
3、用戶必須選中復(fù)選框以接受條款和條件。
?
明確的驗證模型?
修改Home控制器代碼如下圖所示:
public class HomeController : Controller{// GET: Homepublic ViewResult MakeBooking(){return View(new Appointment { Date = DateTime.Now});}[HttpPost]public ViewResult MakeBooking(Appointment appt){if (string.IsNullOrEmpty(appt.ClientName)){ModelState.AddModelError("ClientName", "Please enter yourname");}if (ModelState.IsValidField("Date")){ModelState.AddModelError("Date", "Please enter a date in the future");}if (!appt.TermsAccepted){ModelState.AddModelError("TermsAccepted","You must accepted terms");}if (ModelState.IsValid){return View("Completed", appt);}else{return View();}}}通過使用ModelState.IsValidField屬性,來檢查模型綁定器是否能夠?qū)σ粋€屬性賦值。對于Data屬性就是這么做的,以確保模型綁定器能夠解析用戶所遞交的值。如果無法從請求中解析到一個值,執(zhí)行額外檢查或者報告其他錯誤消息是沒有意義的(當無法從請求數(shù)據(jù)為Date屬性解析到一個值時,該錯誤會在ModelState.IsVaild中體現(xiàn)出來)。
在MakeBooking.cshtml視圖中用來生成input元素的模板視圖輔助器,會檢查視圖模型的驗證錯誤。
如果相關(guān)屬性有錯誤報告,那么輔助器對相應(yīng)的Input元素添加一個CSS的class標簽屬性,其值為input-validation-error.這就是為什么一開始需要在布局頁中設(shè)置CSS樣式的原因。
在不輸入任何數(shù)據(jù)的情況下,提交表單,頁面如下所示:
?
?
顯示驗證消息?
通過顯示驗證消息可以告訴用戶提示的是什么問題,在MakeBooking.cshtml文件中,增加如下圖代碼:
@model ModelVaildation.Models.Appointment <!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>MakeBooking</title> </head> <body> <h4>Book an Appointment</h4>@using (Html.BeginForm()) {@Html.ValidationSummary();<p>Your Name : @Html.EditorFor(m => m.ClientName)</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"/>} </body> </html>Html.ValidationSummary()?輔助器給用戶顯示了驗證錯誤的摘要,如果沒有錯誤,則不會生成任何HTML代碼。
這些錯誤被標識成div元素的一個列表,對該元素運用了validation-summary-errors類,如下圖所示:
? ? ? ? ? ? ? ??
?
ValidationSummary還有一些重載版本,允許開發(fā)人員指定顯示模型級錯誤。前面用的ModelState注冊的錯誤都是屬性級的錯誤。
| 重載方法 | 描述 |
| Html.ValidationSummary() | 生成所有驗證錯誤的摘要 |
| Html.ValidationSummary(bool) | 如果bool參數(shù)為ture,那么只顯示模型級別錯誤。如果為false,那么便顯示所有錯誤。 |
| Html.ValidationSummary(string) | 在所有驗證錯誤摘要之前顯示一條消息(包括在string參數(shù)中) |
| Html.ValidationSummary(bool,string) | 在驗證錯誤前顯示一條消息。如果bool參數(shù)為true,只顯示模型級別的錯誤 |
當存在由于兩個或者多個屬性值之間的相互作用而引發(fā)的錯誤時,可以使用模型級錯誤。舉個例子:假設(shè)姓名為“Joe”的客戶不能形成星期一的預(yù)約,修改Home控制器的代碼如下圖所示:
[HttpPost]public ViewResult MakeBooking(Appointment appt){if (string.IsNullOrEmpty(appt.ClientName)){ModelState.AddModelError("ClientName", "Please enter yourname");}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 accepted terms");}if (ModelState.IsValidField("ClientName") && ModelState.IsValidField("Date") && appt.ClientName == "Joe" && appt.Date.DayOfWeek == DayOfWeek.Monday){ModelState.AddModelError("", "Joe Cannot book appointments OnMondays");}if (ModelState.IsValid){return View("Completed", appt);}else{return View();}}修改MakeBooking.cshtml中代碼如下:
@Html.ValidationSummary(true);運行情況如下:?
? ? ? ? ??
在查看Joe是否企圖預(yù)約在星期一之前,用ModelState.IsValidField方法來確認,已經(jīng)有了合法的ClientName 和Date 值。這意味著,除非前面在屬性上的檢查已經(jīng)成功,否則是不會生成模板級錯誤的。給ModelState.AddModelError方法的第一個參數(shù)傳遞一個空字符串“ ”,便可以注冊一條模型級錯誤。?但是同樣帶來了另一個問題,用戶無法看到單選框未勾選的屬性級錯誤了。
?
顯示屬性級驗證消息?
為了能看到屬性級錯誤,修改MakeBooking.cshtml代碼如下所示:
@model ModelVaildation.Models.Appointment <!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>MakeBooking</title> </head> <body> <h4>Book an Appointment</h4>@using (Html.BeginForm()) {@Html.ValidationSummary(true);<p>@Html.ValidationMessageFor(m => m.ClientName)</p><p>Your Name : @Html.EditorFor(m => m.ClientName)</p>@Html.ValidationMessageFor(m => m.Date)<p>Appointment Date: @Html.EditorFor(m => m.Date)</p>@Html.ValidationMessageFor(m => m.TermsAccepted)<p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms &conditions</p><input type="submit" value="Make Booking"/>} </body> </html>運行結(jié)果如下圖所示:
? ? ? ? ??
如果所運用的屬性有驗證錯誤,輔助器會插入HTML到響應(yīng)中,并生成像這樣的元素:?
?
用元數(shù)據(jù)指定驗證規(guī)則?
MVC框架也支持用源數(shù)據(jù)來表達模型驗證規(guī)則。使用元數(shù)據(jù)的優(yōu)點是,整個應(yīng)用程序中運用綁定過程的任何地方,都會強制執(zhí)行驗證規(guī)則,而不只存在于個別動作方法中。
修改Appointment.cs文件中的代碼:
public class Appointment{[Required]public string ClientName { 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("Myother Property") ] | 兩個屬性必須有同樣的值。當你要求一個用戶對一個屬性提供兩次同樣的值的時候,這個是有用的。例如:一個郵件地址或者一個口令 |
| Range | [ Range(10,20) ] | 一個數(shù)字值(或?qū)崿F(xiàn)了IComparable 的任何屬性類型),必須不超出指定的最小值和最大值。為了指定只要一端的邊界,可以用一個MinValue或者MaxValue常數(shù),如[ Range(int.MaxValue,50)] |
| RegularExpression | [ RegularExprssion(" pattern") ] | 一個字符串值,必須匹配指定的正則表達式模式。注意,該模式必須匹配用戶提供的所有值。而不只是其中一個子串。磨人的,它是大小寫敏感的,但是你可以通過運用(?!)修飾符,是大小寫不敏感——[RegularExpression("(?!)mypattern")] |
| Required | [Required] | 必須是一個非空值。或一個不是只含空格的字符串。如果你希望空格作為可接受值,可以用 [ Required(AllowEmtptyString = true)] |
| StringLength | [StringLength(10)] | 一個字符串值。必須不超過指定的最大長度。也可以指定一個最小長度,[StringLength (10, MinimumLength = 2)] |
?創(chuàng)建自定義的屬性驗證注解屬性
通過從ValidationAttribtue類進行派生,并實現(xiàn)自定義驗證邏輯,也可以添加自己的注解屬性。創(chuàng)建一個Infrastructure文件夾,并在其中創(chuàng)建一個名稱為MustBeTrueAttribute.cs的類文件。如下圖所示:
public class MustBeTrueAttribute:ValidationAttribute{public override bool IsValid(object value){return value is bool && (bool)value;}}使用方法如下圖所示:
[MustBeTrue(ErrorMessage ="You must accept the terms")]public bool TermsAccepted { get; set; }通過內(nèi)建的驗證注解屬性進行派生?
也可以通過內(nèi)建的驗證注解屬性來派生新類,這為我們提供了擴展內(nèi)建驗證注釋屬性的能力。代碼如下圖所示:
public class FutrueDateAttribute:RequiredAttribute{public override bool IsValid(object value){return base.IsValid(value) && ((DateTime)value) > DateTime.Now;}}使用方法如下圖所示:?
[FutrueDate(ErrorMessage ="Please enter a date")]public DateTime Date { get; set; }創(chuàng)建模型驗證注解屬性?
到目前為止,所創(chuàng)建的自定義驗證注解屬性都是運用于個別模型屬性的,這意味著他們只能觸發(fā)屬性級別的驗證錯誤。也可以自定義注解屬性來驗證整個模型,它們將引發(fā)模型級錯誤。代碼如下圖所示:
public class NoJoeOnMondaysAttribute:ValidationAttribute{public NoJoeOnMondaysAttribute(){ErrorMessage = "Joe cannot book appointments on Mondays";}public override bool IsValid(object value){Appointment app = value as Appointment;if (app == null || string.IsNullOrEmpty(app.ClientName) || app.Date == null){//還沒有正確類型的模型要驗證,或者還沒有所需要的ClientName 和 Date 屬性的值return true;}else{return !(app.ClientName == "Joe" && app.Date.DayOfWeek == DayOfWeek.Monday);}}}使用方式如下圖所示:
[NoJoeOnMondays]public class Appointment{[Required]public string ClientName { get; set; }[DataType(DataType.Date)][FutrueDate(ErrorMessage ="Please enter a date")]public DateTime Date { get; set; }[MustBeTrue(ErrorMessage ="You must accept the terms")]public bool TermsAccepted { get; set; }}?使用客戶端驗證
到目前為止,所使用的驗證技術(shù)都是服務(wù)端驗證。這意味著,用戶把他們的數(shù)據(jù)遞交給服務(wù)器,服務(wù)器驗證這些數(shù)據(jù),并返回驗證結(jié)果(要么數(shù)據(jù)處理成功,要么列出需要修正的錯誤)。
在Web應(yīng)用程序中,用戶會期望得到及時的反饋——對服務(wù)器不作任何遞交。這稱為客戶端驗證,而且,這些通常是用JavaScript實現(xiàn)的。用戶輸入的數(shù)據(jù)在被放送給服務(wù)器之前就進行驗證,給用戶提供即時反饋并修正的機會。
客戶端驗證是由Web.Config文件中的兩個設(shè)置來控制的。為了確保驗證生效,必須確保兩個設(shè)置都必須為true如下圖所示:
? ? ? ? ? ??
添加驗證庫JS文件到_Layout.cshtml,順序是很重要的,必須先添加jQuery庫然后是微軟驗證庫文件。
一旦啟用了客戶端驗證,并確保在布局中引用了JavaScript庫,就可以執(zhí)行客戶端驗證了。最簡單的方式是運用前面服務(wù)端驗證所使用的注解屬性,如Required,Range以及StringLength等。?修復(fù)Appointment模型類如下圖所示:
public class Appointment{[Required][StringLength(10,MinimumLength =3)]public string ClientName { get; set; }[DataType(DataType.Date)]public DateTime Date { get; set; }public bool TermsAccepted { get; set; }}運行程序并導(dǎo)航到/Home/MakeBooking,并在name字段中輸入字母X,便可以看到客戶端驗證的效果:?
? ? ? ? ? ? ? ? ? ? ???
在瀏覽器中呈現(xiàn)的反饋是即時的,不需要形成對服務(wù)器的請求。事實上,執(zhí)行驗證的JavaScript代碼會阻止遞交表單,直到不再出現(xiàn)驗證錯誤為止。?
當用戶更改錯誤時,反饋是即時的。如果你返回到name屬性,并不斷輸入,當輸入了三個或者更長的字符時,驗證錯誤便會消失。但是如果繼續(xù)輸入,直到達到十一個字符長度時,便能看到錯誤消息再次提示。
?
?
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的精通ASP.NET MVC ——模型验证的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Elasticsearch修改字段之别名
- 下一篇: windows安装双JDK并实现版本切换