DataAnnotations 簡介
DataAnnotations 是 NET 內建的驗證器,是相當老牌的驗證器,時至今日,依然有新功能不斷添加近來,使用上也相當簡單。只是自從使用 FluentValidation 後,就漸漸少用,趁著整理 FluentVaildation,順便水一篇文章同時做個紀錄並與 FluentValidation 做對照。
1
2
3
4
5
6
7
8
9
|
public class CreateWeatherForecastRequest
{
public string Nation { get; set; }
public string City { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
|
撰寫驗證
內建驗證語法
使用內建的驗證器,好處即是不用安裝,並且若是撰寫 MVC 專案,則 Razor Page頁面可搭配jquery.validate
顯示錯誤。
撰寫風格是在要驗證的屬性上面,加上 ValidationAttribute
,而 NET 也有提供許多內建的 ValidationAttribute,常見如
- 必填:[Required]
- 自訂名稱:[Display(Name = “國家”)]
- 字串長度:[StringLength(50)]
- 最小字串長度:[MinLength(1)]
- 最大字串長度:[MaxLength(100)]
- 數值範圍:[Range (1, 100)]
- Email 格式:[EmailAddress]
更多內建驗證方法,可以直接參考官方文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class CreateWeatherForecastRequest
{
[Display(Name = "國家")]
[Required(ErrorMessage = "{0} 為必填欄位")]
[StringLength(50, ErrorMessage = "{0} 最多可輸入 {1} 字")]
public string Nation { get; set; }
[Display(Name = "城市")]
[Required(ErrorMessage = "{0} 為必填欄位")]
[StringLength(50, ErrorMessage = "{0} 最多可輸入 {1} 字")]
public string City { get; set; }
public DateTime Date { get; set; }
[Display(Name = "溫度")]
[Range(-50, 50, ErrorMessage = "{0} 必須介於 {1} 至 {2}")]
public int TemperatureC { get; set; }
public string Summary { get; set; }
}
|
自訂驗證 IValidatableObject
如果內建的驗證語法無法達到需求,也可以自訂驗證。自訂驗證大致可分為兩種,實作 IValidatableObject
和 自訂ValidationAttribute
。
在要驗證的類別上,實作IValidatableObject
,在 Validate
加上驗證邏輯,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class CreateWeatherForecastRequest : IValidatableObject
{
public string Nation { get; set; }
public string City { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (StartTime > EndTime)
{
yield return new ValidationResult("開始時間必須在結束時間之前", new[] { nameof(StartTime) });
}
}
}
|
自訂驗證 ValidationAttribute
另一種自訂驗證的方法,就是實作 ValidationAttribute
,然後在驗證物件上面加上自訂的Attribute,如:
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
|
public class CreateWeatherForecastRequest
{
public string Nation { get; set; }
public string City { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
[BeforeEndTime]
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class BeforeEndTimeAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var endTime = (DateTime)validationContext.ObjectType.GetProperty("EndTime").GetValue(validationContext.ObjectInstance, null);
if ((DateTime)value > endTime)
{
return new ValidationResult("開始時間必須在結束時間之前");
}
return ValidationResult.Success;
}
}
|
陣列驗證,同樣使用ValidationAttribute
來驗證
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
|
public class CreateWeatherForecastRequest
{
public string Nation { get; set; }
public string City { get; set; }
public string Summary { get; set; }
[TemperatureValidate]
public List<Temperature> Temperatures { get; set; }
}
public class Temperature
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public int TemperatureC { get; set; }
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class TemperatureValidateAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var temperatures = value as List<Temperature>;
foreach (var temperature in temperatures)
{
if (temperature.StartTime > temperature.EndTime)
{
return new ValidationResult("開始時間必須在結束時間之前");
}
}
return ValidationResult.Success;
}
}
|
驗證結果
其他
與資料庫互動
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class EmailIsUniqueValidateAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var email = value as string;
var context = validationContext.GetService<ApplicationDbContext>();
var isExists = context.Users.Any(x => x.Email == email);
// Or
// var userRepository = validationContext.GetService<IUserRepository>();
// var isExists = userRepository.IsEmailUnique(email);
return isExists ? new ValidationResult("Email已被註冊") : ValidationResult.Success;
}
}
|
或者使用 [Remote],參考官方範例
1
2
3
4
5
6
7
8
9
10
|
[AcceptVerbs("GET", "POST")]
public IActionResult VerifyEmail(string email)
{
if (!_userService.VerifyEmail(email))
{
return Json($"Email {email} is already in use.");
}
return Json(true);
}
|
1
2
|
[Remote(action: "VerifyEmail", controller: "Users")]
public string Email { get; set; }
|
參考
System.ComponentModel.DataAnnotations 命名空間
https://learn.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations?view=net-7.0