0%

【MVC教學】7.驗證參數-DataAnnotations

Demo範例 :Git位置

#什麼是DataAnnotations

DataAnnotations是.Net Framework 3.5之後提供的一個命名空間,裡面包含了一些基本的驗證Attribute,同時也提供客製化的方法,希望開發人員能透過簡單的加上Attribute即達到驗證的效果。

1
2
3
4
5
6
7
8
public class UserSignUpParameter
{
/// <summary>
/// 帳號
/// </summary>
[Required]
public string Account { get; set; }
}

#如何使用

我們透過修改先前的範例,嘗試將UserSignUpParameter的驗證從FluentValidation改成用MVC預設提供的DataAnnotations來達成,從修改中學習他是如何運作的。

重新審視一下需求

帳號

  • 必填
  • 必須包含@

密碼

  • 必填
  • 不得小於6個字元

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
{
//
// 摘要:
// 初始化 System.ComponentModel.DataAnnotations.RequiredAttribute 類別的新執行個體。
public RequiredAttribute();

//
// 摘要:
// 取得或設定值,指出是否允許空字串。
//
// 傳回:
// true 如果允許空字串。否則, false。 預設值是 false。
public bool AllowEmptyStrings { get; set; }

//
// 摘要:
// 檢查必要的資料欄位的值不是空的。
//
// 參數:
// value:
// 要驗證的資料欄位值。
//
// 傳回:
// true 如果驗證成功。否則, false。
//
// 例外狀況:
// T:System.ComponentModel.DataAnnotations.ValidationException:
// 資料欄位值為 null。
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
/// <summary>
/// Parameter UserSignUp
/// </summary>
public class UserSignUpParameter
{
/// <summary>
/// 帳號
/// </summary>
[Required(AllowEmptyStrings =true)]
public string Account { get; set; }

/// <summary>
/// 密碼
/// </summary>
public string Password { get; set; }
}

當Account傳入為Null時

/images/20180615/1.png

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

/images/20180615/2.png

接著改傳入空格

/images/20180615/3.png

通過驗證

/images/20180615/4.png

ErrorMessage

如果想修改錯誤的回傳訊息時,可以透過ErrorMessage來處理

1
2
3
4
5
/// <summary>
/// 帳號
/// </summary>
[Required(ErrorMessage = "帳號為必填欄位")]
public string Account { get; set; }

或是

1
2
3
4
5
/// <summary>
/// 帳號
/// </summary>
[Required(ErrorMessage = "{0}為必填欄位")]
public string Account { get; set; }

{0}的位置系統會自動帶入欄位名稱

/images/20180615/5.png

介紹完RequiredAttribute的基本用法後,目前程式碼如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// <summary>
/// Parameter UserSignUp
/// </summary>
public class UserSignUpParameter
{
/// <summary>
/// 帳號
/// </summary>
[Required(ErrorMessage = "{0}為必填欄位")]
public string Account { get; set; }

/// <summary>
/// 密碼.
/// </summary>
[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
{
//
// 摘要:
// 初始化 System.ComponentModel.DataAnnotations.MaxLengthAttribute 類別的新執行個體。
public MaxLengthAttribute();
//
// 摘要:
// 初始化的新執行個體 System.ComponentModel.DataAnnotations.MaxLengthAttribute 類別根據 length
// 參數。
//
// 參數:
// length:
// 陣列或字串資料的最大容許長度。
public MaxLengthAttribute(int length);

//
// 摘要:
// 取得陣列或字串資料所容許的最大長度。
//
// 傳回:
// 陣列或字串資料的最大容許長度。
public int Length { get; }

//
// 摘要:
// 將格式套用到指定的錯誤訊息
//
// 參數:
// name:
// 要包含在格式化字串中的名稱。
//
// 傳回:
// 描述可接受之最大長度的當地語系化字串。
public override string FormatErrorMessage(string name);
//
// 摘要:
// 判斷指定的物件是否有效
//
// 參數:
// value:
// 要驗證的物件。
//
// 傳回:
// 如果此值為 null 或是小於或等於指定的最大長度,則為 true,否則為 false。
//
// 例外狀況:
// T:System.InvalidOperationException:
// 長度為零或小於 –1。
public override bool IsValid(object value);
}

密碼最少應為6個字元設定方式,當然它同時也提供修改ErrorMessage的方法

{0}的位置系統會自動帶入欄位名稱

{1}的位置系統會帶入所設定的最小字元數字

1
2
3
4
5
/// <summary>
/// 密碼.
/// </summary>
[MinLength(6, ErrorMessage = "{0}不得低於{1}個字元")]
public string Password { get; set; }

/images/20180615/6.png

/images/20180615/7.png

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
//
// 摘要:
// 指定 ASP.NET 動態資料的資料欄位值必須符合指定的規則運算式。
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class RegularExpressionAttribute : ValidationAttribute
{
//
// 摘要:
// 初始化 System.ComponentModel.DataAnnotations.RegularExpressionAttribute 類別的新執行個體。
//
// 參數:
// pattern:
// 用來驗證資料欄位值的規則運算式。
//
// 例外狀況:
// T:System.ArgumentNullException:
// pattern 為 null。
public RegularExpressionAttribute(string pattern);

//
// 摘要:
// 取得規則運算式模式。
//
// 傳回:
// 要比對模式。
public string Pattern { get; }
//
// 摘要:
// 取得或設定執行單一比對作業,直到作業逾時之前的時間 (以毫秒為單位)。
//
// 傳回:
// 執行單一比對作業的時間 (以毫秒為單位)。
public int MatchTimeoutInMilliseconds { get; set; }

//
// 摘要:
// 格式化要顯示在規則運算式驗證失敗時的錯誤訊息。
//
// 參數:
// name:
// 造成驗證失敗的欄位名稱。
//
// 傳回:
// 格式化的錯誤訊息。
public override string FormatErrorMessage(string name);
//
// 摘要:
// 會檢查使用者輸入的值是否符合規則運算式模式。
//
// 參數:
// value:
// 要驗證的資料欄位值。
//
// 傳回:
// true 如果驗證成功。否則, false。
//
// 例外狀況:
// T:System.ComponentModel.DataAnnotations.ValidationException:
// 資料欄位值不符合規則運算式模式。
public override bool IsValid(object value);
}

可以透過寫正規表示法的方式來驗證想驗證的屬性,一樣有ErrorMessage可供使用

1
2
3
4
5
/// <summary>
/// 帳號
/// </summary>
[RegularExpression("[@]+",ErrorMessage ="{0}必須包含@字元")]
public string Account { get; set; }

/images/20180615/8.png

/images/20180615/9.png

其它Attribute

DataAnnotations還有提供一些預設的Attribute可以使用,篇幅有限就不一一介紹,有興趣的可以參考以下文章

#客製

如果碰到一個驗證需要在多個地方使用,且預設沒有提供的話,這時候就可以透過自製驗證Attribute來解決,我們以**字元需包含@**為例

繼承ValidationAttribute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MouseCharactersAttribute: ValidationAttribute
{
/// <summary>
/// Returns true if ... is valid.
/// </summary>
/// <param name="value">要驗證之物件的值。</param>
/// <returns>
/// <see langword="true" /> 指定的值是否有效。否則, <see langword="false" />
/// </returns>
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
/// <summary>
/// 帳號
/// </summary>
[MouseCharacters(ErrorMessage ="{0}必須包含@字元")]
public string Account { get; set; }

實測會發現結果是一樣的

/images/20180615/10.png

#比較FluentValidation

DataAnnotations其實使用起來相當方便,且只要知道如何客製ValidationAttribute基本上大部分情境都能解決,加上套用Attribute的方式算是相當親民的寫法。

但我本身基於單一職責的原則下,還是喜歡將驗證邏輯等方法分離到不同的Class管理,而這也正是FluentValidation套件的特性,不過這是見仁見智,就像大部分Pattern一樣,永遠不會有最好的解法,只要能花最小的成本解決需求,同時盡量不犧牲維護及擴充性,那就稱得上是好方法了。