客戶端驗證邏輯會對用戶向表單輸入的數據給出一個即時反饋。而之所以需要服務器端驗證,是因為來自網絡的信息都是不能被信任的。
當在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。
5其他特性
UIHint:
給ASP.NET MVC運行時提供了一個模版名稱,以備調用模版輔助方法(如DisplayFor和EditorFor)渲染輸出時使用。也可以定義自己的模版輔助方法來重寫ASP.NET MVC的默認行為。找不到UIHint指定的模版時,MVC會尋找一個合適的替代模版使用。
HiddenInput:
渲染一個type特性值為“hidden”的輸入元素。