客戶端驗證邏輯會對使用者向表單輸入的資料給出一個即時反饋。而之所以需要伺服器端驗證,是因為來自網路的資訊都是不能被信任的。
當在ASP.NET MVC設計模式上下文中談論驗證時,主要關注的是驗證模型的值
資料註解特性定義在名稱空間System.ComponentModel.DataAnnotations中,它們提供了伺服器端驗證的功能。當在模型的屬性上使用這些屬性時,框架也支援客戶端驗證。在名稱空間DataAnnotations中,有4個特性可以用來應對一般的驗證場合。
1.1 Required
因為客戶的名字是必須的,所以需要在模型類的屬性上新增Required特性
1 [Required] 2 public string FirstName{ get; set;}
當屬性是null或空時,Required特性將會引發一個驗證錯誤。
1.2 StringLength
校驗資料的長度
1 [Required] 2 [StringLength(160)] 3 public string FirstName { get; set;}
當資料長度超過160則會提醒。MinimumLength引數是一個可選項,用於設定字串的最小長度,如下(長度>=3且<=160)
1 [Required] 2 [StringLength(160, MinimumLength=3)] 3 public string FirstName { get; set;}
1.3 RegularExpression
用於正規表示式的驗證,用於郵箱、電話等屬性。
1 [RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}")] 2 public string Email {get; set;}
1.4 Range
Range 特性用來指定數值型別值的最小值和最大值。
限定年齡在35~44之間:
1 [Range(35,44)] 2 public int Age {get; set;}
限定價格在0.00~49.99之間,該過載方法可限定資料型別:
1 [Range(typeof(decimal),"0.00", 49.00)] 2 public decimal Price {get; set;}
1.5 Compare
Compare特性確保模型物件的兩個屬性擁有相同的值,一般用於校驗客戶的重複輸入資料
[RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}")] public string Email {get; set;} [Compare("Email")] public string EmailConfirm {get; set;}
如果兩次輸入的e-mail地址不一致,將會提醒。
1.6 Remote
ASP.NET MVC框架還為應用程式在名稱空間System.Web.Mvc中額外新增了Remote驗證特性。
Remote特性可以利用伺服器端的回撥函式執行客戶端的驗證邏輯。例如避免使用者註冊時在資料庫中產生相同使用者名稱的使用者,可以使用Remote特性把UserName的值傳送到伺服器,然後在伺服器端的資料庫中與相應的表欄位值進行比較:
1 [Remote("CheckUserName", "Account")] 2 public string UserName {get ; set;}
在特性中可以設定客戶端程式碼要呼叫的控制器名稱可操作名稱。客戶端程式碼會自動把使用者輸入的UserName屬性值傳送發到伺服器,該特性的一個過載構造方法還允許指定要傳送給伺服器的其他欄位:
1 public JsonResult CheckUserName (string username) 2 { 3 var result = Membership.FindUserByName(username).Count ==0 ; 4 return Json(result, JsonRequestBehavior.AllowGet); 5 }
上面的控制器操作會利用與UserName屬性同名的引數進行驗證,並返回一個封裝在JacaScript Object Notation(JSON)物件中的布林型別值(true或false)
2.1 自定義錯誤提示訊息及其本地化
每個驗證特性都允許傳遞一個帶有自定義錯誤提示訊息的引數。
1 [Required( ErrorMessage = "Your last name is required")] 2 [StringLength(160, ErrorMessage="Your last name is too long")] 3 public string LastName {get ; set ;}
自定義的錯誤提示訊息在字串中也有一個格式項。內建特性使用友好的屬性顯示名稱格式化錯誤提示訊息字串
1 [Required( ErrorMessage = "Your {0} is required")] 2 [StringLength(160, ErrorMessage="Your last name is too long")] 3 public string LastName {get ; set ;}
如果應用程式是多語言的,硬編碼的方式就不適用了。可以為驗證特性都允許為本地化的錯誤提示訊息指定資源型別和資源名稱。
1 [Required(ErrorMessageResource = typeof(ErrorMessages), 2 ErrorMessageResourceName="LastNameRequired")] 3 [StringLength(160, ErrorMessageResourceType = typeof(ErrorMessage),
ErrorMessageResourceName = "LastNameTooLong")] 4 public string LastName { get; set;}
3.自定義驗證邏輯
兩個核心應用方法:
- 將驗證邏輯封裝在自定義的資料註解中
- 將驗證邏輯封裝在模型物件中
把驗證邏輯封裝在自定義資料註解中可以實現多個模型中重用邏輯,這需要在特性內部編寫程式碼以應對不同型別的模型,但一旦實現,新的註解就可以在多處重用。
另一方面,如果將驗證邏輯直接放入模型物件中,就意味著驗證邏輯可以很容易地編碼實現,因為這樣只需要關心一種模型物件的驗證邏輯,從而方便了對物件的狀態和結構做某些假定,但這種方式不利於實現邏輯的重用。
3.1 自定義註解
所有的驗證註解(如Required和Range)特性最終都派生自基類ValidationAttribute,它是個抽象類,在名稱空間System.ComponentModel.DataAnnotations中定義。所以,程式的驗證邏輯也必須派生自ValidationAttribute類:
1 using System.ComponentModel.DataAnnotations; 2 3 namespace MvcMusicStore.Infrastructure 4 { 5 public clsaa MaxWordsAttribute : ValidationAttribute 6 { 7 ....... 8 } 9 }
為了實現驗證邏輯,至少需要重寫基類中提供的IsValid方法的其中一個版本。重寫IsValid方法時利用的ValidationContext引數,提供了很多可在IsValid方法內部使用的資訊,如模型型別、模型物件例項、用來驗證屬性的人性化顯示名稱以及其他有用的資訊。
此處驗證單詞輸入數量,並設定預設錯誤提示資訊:
1 using System.ComponentModel.DataAnnotations; 2 3 namespace MvcMusicStore.Infrastructure 4 { 5 public clsaa MaxWordsAttribute : ValidationAttribute 6 { 7 public MaxWordsAttribute(int maxWords) : base("{0} has too many words.") 8 { 9 _maxWords = maxWords; 10 } 11 12 protected override ValidationResult IsValid(object value,
ValidationContext validationContext) 13 { 14 var valueAsString = value.ToString(); 15 if( valueAsString.Split(' ').Length > _maxWords) 16 { 17 var errorMessage = FormatErrorMessage(validationContext.DisplayName); 18 return new ValidationResult(errorMessage); 19 } 20 return ValidationResult.Success; 21 }
22 private readonly int _maxWords; 23 } 24 }
FormatErrorMessage可以使用合適的錯誤提示訊息字串(即使這個字串是儲存在一個本地資原始檔中)。這條程式碼語句需要傳遞name屬性的值,這個值可以通過validationContext引數的DisplayName屬性獲得。構造完驗證邏輯後,就可以將其應用到任何模型屬性上:
1 [Required] 2 [StringLength(160)] 3 [MaxWords(10)] 4 public string LastName {get ; set ;}
甚至可以賦予特性自定義的錯誤提示訊息:
1 [Required] 2 [StringLength(160)] 3 [MaxWords(10, ErrorMessage = "There are too many words in {0}")] 4 public string LastName {get ; set ;}
IValidatableObject
自驗證(self-validating)模型是指一個知道如何驗證自身的模型物件。一個模型物件可以通過實現IValidatableObject介面來實現對自身的驗證。下面在Order模型中直接實現對LastName欄位中單次個數的檢查:
1 public class Order : IValidatableObject 2 { 3 public IEnumerable<ValidationResult> Validate(ValidationContext ValidationContext) 4 { 5 if(LastName != null && LastName.Split(' ').Length > 10) 6 { 7 yield return new ValidationResult("The last name has too many words!",
new []{"LastName"}); 8 } 9 //.... 10 } 11 }
這種方式與特性版本有幾個明顯的不同點:
- MVC執行時為執行驗證而呼叫的方法名稱是Validate而不是IsValid,但更重要的是,它們返回型別和引數不同
- Validate的返回型別是IEnumerable<ValidationResult>,而不是單獨的ValidationResult物件。因為從表面上看,內部的驗證邏輯驗證的是整個模型,因此可能返回多個驗證錯誤。
- 這裡沒有value引數傳遞給Validate方法,因為在此Validate是一個模型例項方法,在其內部可以直接訪問當前模型物件的屬性值。
4.顯示和編輯註解
4.1 Display
Display特性可為模型屬性設定友好的“顯示名稱”
1 [Required] 2 [StringLength(160)] 3 [Display(Name="Last Name", Order=15001)] 4 public string FirstName { get; set;}
4.2 ScaffoldColumn
ScaffoldColumn特性可以隱藏HTML輔助方法(如EditorForModel和DisplayForModel)渲染的一些屬性:
1 [ScaffoldColumn(false)] 2 public string Username {get; set;}
新增了這個特性之後,EditorForModel輔助方法將不再為Username欄位顯示輸入元素和Label標籤。然而需要注意的是,如果模型繫結器在請求中看到匹配的值,那麼他仍然會試圖為Username屬性賦值。
4.3 DisplayFormat
通過命名引數,DisplayFormat特性可用來處理屬性的各種格式化選項。當屬性包含空值時,可以提供可選的顯示文字,也可以為包含標記的屬性關閉HTML編碼,還可以為執行時指定一個應用於屬性值的格式化字串。
下面的程式碼可將模型的Total屬性值格式化為貨幣值形式:
1 [DisplayFormat ( ApplyFormatInEditMode = true, DataFormatString "{0:c}")] 2 public decimal Total {get; set;}
4.4 ReadOnly
如果需要確保預設的模型繫結器不使用請求中的新值來更新屬性,可在屬性上新增ReadOnly特性:
1 [ReadOnly(true)] 2 public decimal Total {get; set;}
4.5 DataType
DataType特性可為執行時提供關於屬性的特定用途資訊。例如,String型別的屬性可應用與e-mail地址、URL或是密碼。DataType特性可滿足所有這些需求:
1 [Required] 2 [DataType(DataType.Password)] 3 [Display(name="Password")] 4 public string Password {get; set;}
其他的資料型別還有Currency、Date、Time和MultilineText。