#什麼是DataAnnotations
DataAnnotations是.Net Framework 3.5之後提供的一個命名空間,裡面包含了一些基本的驗證Attribute,同時也提供客製化的方法,希望開發人員能透過簡單的加上Attribute即達到驗證的效果。
1 2 3 4 5 6 7 8
| public class UserSignUpParameter { [Required] public string Account { get; set; } }
|
#如何使用
我們透過修改先前的範例,嘗試將UserSignUpParameter的驗證從FluentValidation改成用MVC預設提供的DataAnnotations來達成,從修改中學習他是如何運作的。
重新審視一下需求
RequiredAttribute
RequiredAttribute為DataAnnotations預設提供的驗證方式,目標是驗證該欄位是否為Null或Empty
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] public class RequiredAttribute : ValidationAttribute { public RequiredAttribute();
public bool AllowEmptyStrings { get; set; }
public override bool IsValid(object value); }
|
從上述的程式碼可以看出它有提供AllowEmptyStrings的屬性可以設定,意義如同字面意思,可以EmptyString。
AllowEmptyStrings
我們先將Account掛上Required,並且指定屬性AllowEmptyStrings為True,執行看看
繼承自Attribute的類別,在掛Attribute時可以省略後綴詞,所以可以看到程式碼只寫Required而不是RequiredAttribute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class UserSignUpParameter { [Required(AllowEmptyStrings =true)] public string Account { get; set; }
public string Password { get; set; } }
|
當Account傳入為Null時

ModelState.IsValidate為False,在Values.Errors底下可以找到錯誤訊息Account 欄位是必要項。

接著改傳入空格

通過驗證

ErrorMessage
如果想修改錯誤的回傳訊息時,可以透過ErrorMessage來處理
1 2 3 4 5
|
[Required(ErrorMessage = "帳號為必填欄位")] public string Account { get; set; }
|
或是
1 2 3 4 5
|
[Required(ErrorMessage = "{0}為必填欄位")] public string Account { get; set; }
|
{0}的位置系統會自動帶入欄位名稱

介紹完RequiredAttribute的基本用法後,目前程式碼如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public class UserSignUpParameter { [Required(ErrorMessage = "{0}為必填欄位")] public string Account { get; set; }
[Required(ErrorMessage = "{0}為必填欄位")] public string Password { get; set; } }
|
MinLengthAttribute
MinLengthAttribute是用來檢查屬性的最小字數,與MaxLengthAttribute為剛好相反的一組
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] public class MaxLengthAttribute : ValidationAttribute { public MaxLengthAttribute(); public MaxLengthAttribute(int length);
public int Length { get; }
public override string FormatErrorMessage(string name); public override bool IsValid(object value); }
|
密碼最少應為6個字元設定方式,當然它同時也提供修改ErrorMessage的方法
{0}的位置系統會自動帶入欄位名稱
{1}的位置系統會帶入所設定的最小字元數字
1 2 3 4 5
|
[MinLength(6, ErrorMessage = "{0}不得低於{1}個字元")] public string Password { get; set; }
|


RegularExpression
Account還有一個需求是必須包含@字元,這時候可以透過預設提供的RegularExpression來達成需求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] public class RegularExpressionAttribute : ValidationAttribute { public RegularExpressionAttribute(string pattern);
public string Pattern { get; } public int MatchTimeoutInMilliseconds { get; set; }
public override string FormatErrorMessage(string name); public override bool IsValid(object value); }
|
可以透過寫正規表示法的方式來驗證想驗證的屬性,一樣有ErrorMessage可供使用
1 2 3 4 5
|
[RegularExpression("[@]+",ErrorMessage ="{0}必須包含@字元")] public string Account { get; set; }
|


其它Attribute
DataAnnotations還有提供一些預設的Attribute可以使用,篇幅有限就不一一介紹,有興趣的可以參考以下文章
#客製
如果碰到一個驗證需要在多個地方使用,且預設沒有提供的話,這時候就可以透過自製驗證Attribute來解決,我們以**字元需包含@**為例
繼承ValidationAttribute
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class MouseCharactersAttribute: ValidationAttribute { public override bool IsValid(object value) { return base.IsValid(value); } }
|
Override IsValid Method
1 2 3 4 5 6 7 8 9 10 11
| public override bool IsValid(object value) { if (value == null) { return false; }
var stringValue = value as string;
return stringValue.Contains("@"); }
|
將原本的RegularExpression改成我們客製的MouseCharacters
1 2 3 4 5
| [MouseCharacters(ErrorMessage ="{0}必須包含@字元")] public string Account { get; set; }
|
實測會發現結果是一樣的

#比較FluentValidation
DataAnnotations其實使用起來相當方便,且只要知道如何客製ValidationAttribute基本上大部分情境都能解決,加上套用Attribute的方式算是相當親民的寫法。
但我本身基於單一職責的原則下,還是喜歡將驗證邏輯等方法分離到不同的Class管理,而這也正是FluentValidation套件的特性,不過這是見仁見智,就像大部分Pattern一樣,永遠不會有最好的解法,只要能花最小的成本解決需求,同時盡量不犧牲維護及擴充性,那就稱得上是好方法了。