一.學習前的一句話
在這里要先感謝那些能夠點開我隨筆的博友們。慢慢的已經在博客園中度過一年半了,伊始只是將博客園作為自己學習的記錄本一樣使用,也不敢將自己的隨筆發表到博客園首頁,生怕自己的技藝不高,反倒成了笑話。但是隨着時間的推移,再也按捺不住這種想法,於是就寫了一篇隨筆發表到博客園首頁。讓我意想不到的是有許多人都看了,而且也留下了評論。這讓我鼓起勇氣寫了第二、三、四篇。到現在的連載,這里我希望那些從未發表過隨筆的人可以嘗試去發表,在這里他人不會嘲諷你,而是會給你更好的建議。說了這么多下面我們繼續開始學習ASP.NET MVC吧。
二.准備工作
1、創建一個ASP.NET MVC 4網站(筆者的命名是MvcStudy)
2、在Models下創建一個Register模型類,具體代碼如下所示:
1 namespace MvcStudy.Models 2 { 3 public class Register 4 { 5 public String UserName { get; set; } 6 public String Password { get; set; } 7 public String RptPassword { get; set; } 8 public String Email { get; set; } 9 public DateTime BirthDate { get; set; } 10 public bool IsApprove { get; set; } 11 } 12 }
3、創建一個名為Home的控制器,並在其中寫入下面的代碼:
1 namespace MvcStudy.Controllers 2 { 3 public class HomeController : Controller 4 { 5 public ActionResult Index() 6 { 7 return View(); 8 } 9 10 [HttpPost] 11 public ActionResult Index(Register reg) 12 { 13 return View(); 14 } 15 } 16 }
4、接着在Views下創建一個Home文件夾並在其中新建一個Index視圖,代碼如下:
1 @model MvcStudy.Models.Register 2 @{ 3 ViewBag.Title = "Index"; 4 } 5 6 7 @using (Html.BeginForm()) 8 { 9 <div> 10 用戶名: 11 @Html.EditorFor(m => m.UserName) 12 </div> 13 <div> 14 密碼: 15 @Html.EditorFor(m => m.Password) 16 </div> 17 <div> 18 重復密碼: 19 @Html.EditorFor(m => m.RptPassword) 20 </div> 21 <div> 22 出生日期: 23 @Html.EditorFor(m => m.BirthDate) 24 </div> 25 <div> 26 郵箱: 27 @Html.EditorFor(m => m.Email) 28 </div> 29 <div> 30 @Html.EditorFor(m => m.IsApprove) 31 同意相關條約 32 </div> 33 <div> 34 <input type="submit" value="注冊" /> 35 </div> 36 }
5、因為后面要用到客戶端驗證,所以這里我們先把需要引用需要的js庫(在Views/Shared/_Layout.cshtml中寫入):
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <meta name="viewport" content="width=device-width" /> 6 <title>@ViewBag.Title</title> 7 @Styles.Render("~/Content/css") 8 @Scripts.Render("~/bundles/modernizr") 9 @Scripts.Render("~/bundles/lufy") 10 </head> 11 <body> 12 13 @RenderBody() 14 15 @Scripts.Render("~/bundles/jquery") 16 @Scripts.Render("~/bundles/jqueryval") 17 @RenderSection("scripts", required: false) 18 </body> 19 </html>
PS:為了確保正確,請讀者驗證下web.config中的以下屬性的值是否跟筆者的一樣:

三.常規驗證
相信很多從事ASP.NET的開發者在對數據的驗證上基本都是用的ASP.NET自帶的驗證控件,同時在后台還會通過N多個if語句再去判斷,所以在ASP.NET MVC的常規驗證跟這個一樣,唯一的區別就是錯誤的信息輸出不需要我們自己實現了,下面我們修改Home控制器中的Index(Register reg)動作:
1 [HttpPost] 2 public ActionResult Index(Register reg) 3 { 4 if (String.IsNullOrEmpty(reg.UserName)) 5 { 6 ModelState.AddModelError("UserName", "用戶名不能為空"); 7 } 8 else if (reg.UserName.Length < 6) 9 { 10 ModelState.AddModelError("UserName", "用戶名長度不能小於6位"); 11 } 12 if (ModelState.IsValidField("BirthDate") && reg.BirthDate > DateTime.Now) 13 { 14 ModelState.AddModelError("BirthDate", "生日不能為將來的時間"); 15 } 16 if (ModelState.IsValid) 17 { 18 //保存數據 19 } 20 return View(); 21 }
上面我們簡單的判斷了用戶名是否為空,長度是否小於6,以及出生日期是否填寫的為將來的日期,接着我們還要在Index視圖中加入@Html.ValidationSummary(),這樣我們才能夠看到最后的輸出的錯誤信息,編譯然后不輸入任何內容點擊注冊之后將會出現下面的情況:

我們會發現表單壓根就提交不了,這是因為客戶端驗證在工作。獲取讀者會很奇怪這節只是常規驗證,這個是因為出生日期的格式是DateTime是不能為NULL的,而ASP.NET MVC默認情況下就已經為我們做好了。隨便輸入123到出生日期然后點擊注冊,頁面會反饋下面的錯誤信息:

第一個信息就是我們在控制器中通過if判斷語句加進去的,而第二個似乎你會困惑是怎么回事,這是因為模型綁定器中會為我們進行簡單的驗證,比如日期不能為空,並且日期的格式要正確,這個都是默認的行為。我們可以嘗試在用戶名中輸入123,同時出生日期輸入2020/1/1,點擊注冊,這時候的錯誤信息都是我們添加的了:

讀者使用過很多系統,錯誤信息基本上都是顯示在對應的輸入框的右邊,在ASP.NET MVC中一樣可以判斷,下面我們修改Index視圖:
1 <div> 2 用戶名: 3 @Html.EditorFor(m => m.UserName) 4 @Html.ValidationMessageFor(m => m.UserName) 5 </div> 6 <div> 7 出生日期: 8 @Html.EditorFor(m => m.BirthDate) 9 @Html.ValidationMessageFor(m => m.BirthDate) 10 </div>
這個時候我們在重新提交,錯誤信息就到右邊了。但是筆者還不打算結束掉這節,我如果限制用戶名不能為100000怎么辦呢?或許讀者馬上就能寫出來,但是這個是模型級的錯誤,並不是針對這個字段,所以我們在Home控制器的Index方法(響應Post的那個)中繼續追加:

接着修改Index視圖:

然后重新編譯,用戶名輸入100000就可以看到下面的結果:

這樣我們就可以結束這節了。
四.采用注解屬性的驗證
上面這種方式的驗證雖然簡單,很多人都能夠立馬上手,但是看到動作中N多個if語句的確不是個滋味,在這個炎炎夏日會讓人非常暴躁,這節我們就來簡單的方法來解決這些問題,為我們降溫,我們修改Register模型類:
1 namespace MvcStudy.Models 2 { 3 public class Register 4 { 5 [Required(ErrorMessage="用戶名必須填寫")] 6 [MinLength(6,ErrorMessage="用戶名長度過短")] 7 public String UserName { get; set; } 8 [DataType(DataType.Password)] 9 public String Password { get; set; } 10 [DataType(DataType.Password)] 11 [Compare("Password",ErrorMessage="密碼要一致")] 12 public String RptPassword { get; set; } 13 public String Email { get; set; } 14 public DateTime BirthDate { get; set; } 15 public bool IsApprove { get; set; } 16 } 17 }
筆者在這里還判斷了密碼和重復密碼是否相同,關於DataType可以參考筆者寫的模型綁定,下面重新編譯,然后打開頁面測試,就可以發現這些驗證都實現了,因為筆者這里默認開啟了客戶端驗證所以在未驗證通過的情況下無法提交表單。但是我們發現現有的驗證注解屬性沒法實現顯示出生日期不能為將來實現,所以下面一節我們還要學習自定義驗證注解屬性。
五.自定義驗證注解屬性
通過上節我們已經能夠使用ASP.NET MVC自帶的驗證屬性來完成一些簡單的驗證,正如上節最后所說的那樣,對於一部分驗證自帶的已經無法滿足我們的需求了,那么我們就需要通過自定義的方式來解決,下面我們自定義一個注解屬性來解決上節遺留下來的問題,首先我們新建一個Validation文件夾,然后在該文件夾下面新建一個FutureTimeAttribute類,代碼如下所示:
1 namespace MvcStudy.Validation 2 { 3 public class FutureTimeAttribute : ValidationAttribute 4 { 5 public override bool IsValid(object value) 6 { 7 DateTime dt = (DateTime)value; 8 if (dt != null) 9 { 10 if (dt < DateTime.Now) 11 { 12 return true; 13 } 14 } 15 return false; 16 } 17 } 18 }
接着我們就可以運用到對應的屬性上面了
重新編譯,然后填寫一個將來的時間點擊注冊之后我們將會看到如下的結果:

通過這節的補充,相信大家此時此刻酷爽嗎?當然還有一部分人還需要更實現更復雜的驗證判斷,而且是針對特定的模型類實現的,並不適合采用上面這種方式,那么下節會非常符合你的胃口。
PS:讀者不僅僅可以通過繼承ValidationAttribute,同時還可以繼承其他現有的驗證注解屬性,比如RequiredAttribute等。
六.模型與驗證合二為一
我們可以發現筆者之前不是顯示用戶名不能為100000嗎,但是我並沒有將這個作為注解屬性而寫,因為注解屬性一般適合於很多地方都需要使用這種驗證才適合,但是這個限制僅僅只是針對這個模型類,而其他的模型類並不需要。那么我們就需要一中能夠與模型類緊密相關的驗證,而解決方案就是讓模型類實現IValidatableObject接口的Validate方法即可,比如下面筆者將實現限制用戶名不能為100000的情況:
1 namespace MvcStudy.Models 2 { 3 public class Register : IValidatableObject 4 { 5 [Required(ErrorMessage="用戶名必須填寫")] 6 [MinLength(6,ErrorMessage="用戶名長度過短")] 7 public String UserName { get; set; } 8 [DataType(DataType.Password)] 9 public String Password { get; set; } 10 [DataType(DataType.Password)] 11 [Compare("Password",ErrorMessage="密碼要一致")] 12 public String RptPassword { get; set; } 13 public String Email { get; set; } 14 [FutureTimeAttribute(ErrorMessage="時間不能為將來")] 15 public DateTime BirthDate { get; set; } 16 public bool IsApprove { get; set; } 17 18 public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 19 { 20 var result = new List<ValidationResult>(); 21 if (UserName == "100000") 22 { 23 result.Add(new ValidationResult("用戶名不能為100000")); 24 } 25 return result; 26 } 27 } 28 }
然后我們重新編譯,用戶名輸入為100000就可以看到這個錯誤了:

七.客戶端驗證
其實前面幾節一直都在使用客戶端驗證,本該這節是不需要的,但是我們可以發現郵箱部分還沒有驗證,讀者可能會認為應該使用RegularExpression來驗證,但是學過jquery驗證庫的人應該知道這個庫已經自帶了專門的驗證,而這節就是用來手動使用這個驗證的,我們打開Index視圖修改郵箱部分:
1 <div> 2 郵箱: 3 @Html.TextBoxFor(m => m.Email, new 4 { 5 data_val = "true", 6 data_val_email = "郵箱格式錯誤", 7 data_val_required = "請輸入郵箱" 8 }) 9 @Html.ValidationMessageFor(m => m.Email) 10 </div>
刷新頁面,這個時候我們發現郵箱也可以驗證了:

相信很多喜歡客戶端開發而不是服務端開發人員來說,這種方式對於你們來說更快捷,但是對於服務端開發者來說,並不需要氣餒,ASP.NET MVC也提供對應的方法,下節我們將用服務端的方式來實現同樣的效果。
八.自定義客戶端驗證
這里筆者就不多說廢話了直接上代碼,我們自己實現一個郵箱驗證屬性,而且還能夠支持客戶端驗證,首先在Valudation文件夾下新建一個EmailAttribute類,並在其中寫入如下代碼:
1 namespace MvcStudy.Validation 2 { 3 public class EmailAttribute : ValidationAttribute , IClientValidatable 4 { 5 6 public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) 7 { 8 return new List<ModelClientValidationRule> 9 { 10 new ModelClientValidationRule{ 11 ValidationType = "email", 12 ErrorMessage = "請輸入正確的郵箱" 13 } 14 }; 15 } 16 } 17 }
PS:為了能夠符合本節,所以筆者就沒有將服務端的驗證代碼寫到其中,如果讀者需要在真實場合中使用務必將服務端的驗證也要加上去。
下面我們就在Email中加上這個注解屬性並重新編譯,我們可以看到最后頁面的效果跟上一節的效果是完全一致的。
九.結束語
其實筆者並不才,但是擁有非常大的興趣,而今已經是習慣了。每天總是要看上那么些技術方面的書,寫上幾段代碼,當然還不少謝謝隨筆和大家分享啦!
