目錄
系列文章
[Asp.net MVC]Asp.net MVC5系列——第一個項目
[Asp.net MVC]Asp.net MVC5系列——添加視圖
[Asp.net MVC]Asp.net MVC5系列——添加模型
[Asp.net MVC]Asp.net MVC5系列——從控制器訪問模型中的數據
[Asp.net MVC]Asp.net MVC5系列——添加數據
概述
上篇文章中介紹了添加數據,在提交表單的數據的時候,我們需要對數據的合法性進行校驗,Asp.net MVC5中,提供一種方便的驗證方式。本文介紹如何在我們的Student模型中添加一些驗證規則,同時確認當用戶使用我們的應用程序創建或編輯學生信息時將使用這些驗證規則對用戶輸入的信息進行檢查。
DRY原則
在ASP.NET MVC中,有一條作為核心的原則,就是DRY(“Don’t Repeat Yourself,中文意思為:不要讓開發者重復做同樣的事情)原則。ASP.NET MVC提倡讓開發者“一處定義、處處可用”。這樣可以減少開發者的代碼編寫量,同時也更加便於代碼的維護。
在模型中添加驗證規則
首先,讓我們在Student類中追加一些驗證規則。
打開Student.cs文件,在文件的頭部追加一條引用System.ComponentModel.DataAnnotations命名空間的using語句,代碼如下所示。
1 using System.ComponentModel.DataAnnotations;
這個System.ComponentModel.DataAnnotations命名空間是.NET Framework中的一個命名空間。它提供了很多內建的驗證規則,你可以對任何類或屬性顯式指定這些驗證規則。
現在讓我們來修改Student類,增加一些內建的Required(必須輸入),StringLength(輸入字符長度)與Range(輸入范圍)驗證規則,當然,我們也可以自定義我們自己的驗證規則,之后會說明如何創建自定義驗證規則。
1 //------------------------------------------------------------------------------ 2 // <auto-generated> 3 // 此代碼已從模板生成。 4 // 5 // 手動更改此文件可能導致應用程序出現意外的行為。 6 // 如果重新生成代碼,將覆蓋對此文件的手動更改。 7 // </auto-generated> 8 //------------------------------------------------------------------------------ 9 10 namespace Wolfy.FirstMVCProject.Models 11 { 12 using System; 13 using System.Collections.Generic; 14 using System.ComponentModel.DataAnnotations; 15 16 public partial class Student 17 { 18 public Student() 19 { 20 this.Score = new HashSet<Score>(); 21 } 22 23 public int stuId { get; set; } 24 [Required(ErrorMessage = "必須輸入標題")] 25 public string stuName { get; set; } 26 27 public string stuSex { get; set; } 28 public System.DateTime stuBirthdate { get; set; } 29 public System.DateTime stuStudydate { get; set; } 30 [StringLength(4, ErrorMessage = "只能輸入4個字符")] 31 public string stuAddress { get; set; } 32 [Required(ErrorMessage = "必須輸入標題")] 33 //正則驗證 34 [RegularExpression(@"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$", ErrorMessage = "郵箱格式不正確")] 35 public string stuEmail { get; set; } 36 [RegularExpression(@"\d{11}", ErrorMessage = "郵箱格式不正確")] 37 public string stuPhone { get; set; } 38 public Nullable<bool> stuIsDel { get; set; } 39 public Nullable<System.DateTime> stuInputtime { get; set; } 40 public int classId { get; set; } 41 42 public virtual Course Course { get; set; } 43 public virtual ICollection<Score> Score { get; set; } 44 } 45 }
上述這些驗證屬性指定了我們想要強加給模型中各屬性的驗證規則。Required屬性表示必須要指定一個屬性值,在上例中,一個有效的學生信息必須含有標題,地址,電話,郵箱。Range屬性表示屬性值必須在一段范圍之間。StringLength屬性表示一個字符串屬性的最大長度或最短長度。
Action中的代碼
1 [HttpPost] 2 public ActionResult Create(Student student) 3 { 4 //ModelState.IsValid校驗客戶端數據是否全部符合驗證規則 5 if (ModelState.IsValid) 6 { 7 //獲取dropdownlist選中的value值 8 string strClassID = Request.Form["class"]; 9 int intId = Convert.ToInt32(strClassID); 10 var course = from c in entity.Course 11 where c.classId == intId 12 select c; 13 //處理外鍵關系 14 student.Course = course.FirstOrDefault(); 15 entity.Student.Add(student); 16 entity.SaveChanges(); 17 return RedirectToAction("Index"); 18 19 } 20 else 21 { 22 //在不符合驗證規則的時候,得重新綁定DropDownList數據源。 23 var courses = from s in entity.Course 24 select s; 25 List<SelectListItem> items = new List<SelectListItem>(); 26 foreach (var item in courses) 27 { 28 SelectListItem selectListItem = new SelectListItem() { Text = item.className, Value = item.classId.ToString() }; 29 items.Add(selectListItem); 30 } 31 ViewData["class"] = items; 32 return View(student); 33 } 34 }
運行效果
自定義驗證規則
如果上面的驗證規則,不能滿足需要,可以自己定義驗證規則,首先看一下,Required是如何實現的,咱們就模仿Required來實現一個自己的驗證規則。
1 namespace System.ComponentModel.DataAnnotations 2 { 3 // 摘要: 4 // 指定需要數據字段值。 5 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] 6 public class RequiredAttribute : ValidationAttribute 7 { 8 // 摘要: 9 // 初始化 System.ComponentModel.DataAnnotations.RequiredAttribute 類的新實例。 10 public RequiredAttribute(); 11 12 // 摘要: 13 // 獲取或設置一個值,該值指示是否允許空字符串。 14 // 15 // 返回結果: 16 // 如果允許空字符串,則為 true;否則為 false。 默認值為 false。 17 public bool AllowEmptyStrings { get; set; } 18 19 // 摘要: 20 // 檢查必填數據字段的值是否不為空。 21 // 22 // 參數: 23 // value: 24 // 要驗證的數據字段值。 25 // 26 // 返回結果: 27 // 如果驗證成功,則為 true;否則為 false。 28 // 29 // 異常: 30 // System.ComponentModel.DataAnnotations.ValidationException: 31 // 數據字段值為 null。 32 public override bool IsValid(object value); 33 } 34 }
1 // 摘要: 2 // 作為所有驗證屬性的基類。 3 // 4 // 異常: 5 // System.ComponentModel.DataAnnotations.ValidationException: 6 // 在設置非本地化 System.ComponentModel.DataAnnotations.ValidationAttribute.ErrorMessage 7 // 屬性錯誤消息的同時,本地化錯誤消息的 System.ComponentModel.DataAnnotations.ValidationAttribute.ErrorMessageResourceType 8 // 和 System.ComponentModel.DataAnnotations.ValidationAttribute.ErrorMessageResourceName 9 // 屬性也被設置。 10 public abstract class ValidationAttribute : Attribute 11 { 12 //其他代碼 13 }
所以,驗證規則特性必須集成ValidationAttribute類,當然也可以繼承該類的子類。(注意:對於特性約定以Attribute結尾)。
我們就自定義一個驗證類,實現默認值約束規則DefaultsAttribute,而實現最簡單的方式就是自定義正則表達式的規則。
定義的驗證類的代碼如下:
1 public class DefaultsAttribute : RegularExpressionAttribute 2 { 3 public DefaultsAttribute() 4 : base("[mf]") 5 { 6 7 } 8 public override string FormatErrorMessage(string name) 9 { 10 return "性別只能輸入m(男)或者f(女)"; 11 } 12 }
為Student類中的stuSex加上特性。
1 [Defaults] 2 public string stuSex { get; set; }
然后測試一下,看看實現效果
在Create視圖(追加學生信息視圖)與Create方法內部是如何實現驗證的?
該方法中的ModelState.IsValid屬性用來判斷是否提交的學生信息數據中包含有任何沒有通過數據驗證的無效數據。如果存在無效數據,Create方法重新返回追加學生信息視圖。如果數據全部有效,則將該條數據保存到數據庫中。
我們之前創建的使用支架模板的Create.cshtml視圖模板中的代碼顯示如下,在首次打開追加學生信息視圖與數據沒有通過驗證時,Create方法中返回的視圖都是使用的這個視圖模板。
1 @model Wolfy.FirstMVCProject.Models.Student 2 3 @{ 4 Layout = null; 5 } 6 7 <!DOCTYPE html> 8 9 <html> 10 <head> 11 <meta name="viewport" content="width=device-width" /> 12 <title>Create</title> 13 </head> 14 <body> 15 @using (Html.BeginForm("Create","Student",FormMethod.Post)) 16 { 17 @Html.AntiForgeryToken() 18 19 <div class="form-horizontal"> 20 <h4>Student</h4> 21 <hr /> 22 @Html.ValidationSummary(true) 23 24 <div class="form-group"> 25 @Html.LabelFor(model => model.stuName, new { @class = "control-label col-md-2" }) 26 <div class="col-md-10"> 27 @Html.EditorFor(model => model.stuName) 28 @Html.ValidationMessageFor(model => model.stuName) 29 </div> 30 </div> 31 32 <div class="form-group"> 33 @Html.LabelFor(model => model.stuSex, new { @class = "control-label col-md-2" }) 34 <div class="col-md-10"> 35 @Html.EditorFor(model => model.stuSex) 36 @Html.ValidationMessageFor(model => model.stuSex) 37 </div> 38 </div> 39 40 <div class="form-group"> 41 @Html.LabelFor(model => model.stuBirthdate, new { @class = "control-label col-md-2" }) 42 <div class="col-md-10"> 43 @Html.EditorFor(model => model.stuBirthdate) 44 @Html.ValidationMessageFor(model => model.stuBirthdate) 45 </div> 46 </div> 47 48 <div class="form-group"> 49 @Html.LabelFor(model => model.stuStudydate, new { @class = "control-label col-md-2" }) 50 <div class="col-md-10"> 51 @Html.EditorFor(model => model.stuStudydate) 52 @Html.ValidationMessageFor(model => model.stuStudydate) 53 </div> 54 </div> 55 56 <div class="form-group"> 57 @Html.LabelFor(model => model.stuAddress, new { @class = "control-label col-md-2" }) 58 <div class="col-md-10"> 59 @Html.EditorFor(model => model.stuAddress) 60 @Html.ValidationMessageFor(model => model.stuAddress) 61 </div> 62 </div> 63 64 <div class="form-group"> 65 @Html.LabelFor(model => model.stuEmail, new { @class = "control-label col-md-2" }) 66 <div class="col-md-10"> 67 @Html.EditorFor(model => model.stuEmail) 68 @Html.ValidationMessageFor(model => model.stuEmail) 69 </div> 70 </div> 71 72 <div class="form-group"> 73 @Html.LabelFor(model => model.stuPhone, new { @class = "control-label col-md-2" }) 74 <div class="col-md-10"> 75 @Html.EditorFor(model => model.stuPhone) 76 @Html.ValidationMessageFor(model => model.stuPhone) 77 </div> 78 </div> 79 80 <div class="form-group"> 81 @Html.LabelFor(model => model.stuIsDel, new { @class = "control-label col-md-2" }) 82 <div class="col-md-10"> 83 @Html.EditorFor(model => model.stuIsDel) 84 @Html.ValidationMessageFor(model => model.stuIsDel) 85 </div> 86 </div> 87 88 <div class="form-group"> 89 @Html.LabelFor(model => model.stuInputtime, new { @class = "control-label col-md-2" }) 90 <div class="col-md-10"> 91 @Html.EditorFor(model => model.stuInputtime) 92 @Html.ValidationMessageFor(model => model.stuInputtime) 93 </div> 94 </div> 95 96 <div class="form-group"> 97 @Html.LabelFor(model => model.Course.classId, "stuClass", new { @class = "control-label col-md-2" }) 98 <div class="col-md-10"> 99 @Html.DropDownList("class", String.Empty) 100 @Html.ValidationMessageFor(model => model.classId) 101 </div> 102 </div> 103 104 <div class="form-group"> 105 <div class="col-md-offset-2 col-md-10"> 106 <input type="submit" value="Create" class="btn btn-default" /> 107 </div> 108 </div> 109 </div> 110 } 111 112 <div> 113 @Html.ActionLink("Back to List", "Index") 114 </div> 115 </body> 116 </html>
請注意在這段代碼中使用了許多Html.EditorFor幫助器來為Student類的每個屬性輸出一個輸入文本框。在每個Html.EditorFor幫助器之后緊跟着一個Html.ValidationMessageFor幫助器。這兩個幫助器將與從控制器傳入的模型類的對象實例(在本示例中為Student對象的一個實例)結合起來,自動尋找指定給模型的各個驗證屬性,然后顯示對應的驗證錯誤信息。
這種驗證體制的好處是在於控制器和Create視圖(追加學生信息視圖)事先都即不知道實際指定的驗證規則,也不知道將會顯示什么驗證錯誤信息。驗證規則和錯誤信息只在Student類中被指定。
如果我們之后想要改變驗證規則,我們也只要在一處地方進行改變就可以了。我們不用擔心整個應用程序中存在驗證規則不統一的問題,所有的驗證規則都可以集中在一處地方進行指定,然后在整個應用程序中使用這些驗證規則。這將使我們的代碼更加清晰明確,更加具有可讀性、可維護性與可移植性。這將意味着我們的代碼是真正符合DRY原則(一處指定,到處可用)的。
伙伴類
這個demo中,我們使用的是EF的方式操作數據庫的,如果數據庫中表的字段有變化時,我們就要更新模型,如圖所示:
如果從數據庫中更新模型,就會把上面添加的規則給替換掉,那么之前寫的驗證規則就白費了,那么有沒有解決辦法呢?辦法是有的,就是將要說的伙伴類。伙伴類的定義如下,
1 public class StudentValidate 2 { 3 public int stuId { get; set; } 4 [Required(ErrorMessage="姓名是必須的")] 5 public string stuName { get; set; } 6 public string stuSex { get; set; } 7 public System.DateTime stuBirthdate { get; set; } 8 public System.DateTime stuStudydate { get; set; } 9 public string stuAddress { get; set; } 10 public string stuEmail { get; set; } 11 public string stuPhone { get; set; } 12 public Nullable<bool> stuIsDel { get; set; } 13 public Nullable<System.DateTime> stuInputtime { get; set; } 14 public int classId { get; set; } 15 } 16 //指定要與數據模型類關聯的元數據類。 17 [MetadataType(typeof(StudentValidate))] 18 public partial class Student 19 { 20 21 } 22 }

總結
本文介紹了表單驗證的方式,當然,你也可以使用客戶端的校驗方式,表單驗證插件也挺多的,這里就不再贅述了。另外介紹了自定義的驗證規則,在驗證方式不符合自己要求的時候,可以自定義一個驗證規則,當然你可以直接使用正則表達式的驗證規則,但是如果這個驗證規則經常使用,那么將它定義為一個規則特性,像使用Required一樣去使用,是不是更方便?在文章最后,介紹了一種由於更新model造成原來的model改變,修改了原來的驗證規則,可以通過伙伴類來保證原來的驗證規則不變,又能更新model。