# 模型验证 (07_Validation)
最后更新: 2024-09-20
# 📚 概述
DarkM 模型验证模块基于 FluentValidation 构建,提供强类型的模型验证功能,支持自定义验证规则、异步验证等。
源代码位置: DarkM/src/Framework/Validation
# 🏗️ 模块架构
# 完整目录结构
Validation/
├── Validation.Abstractions/
│ ├── IValidateResultFormatHandler.cs # 验证结果格式化接口
│ └── ValidateResultFormatAttribute.cs # 验证结果格式化特性
│
└── Validation.FluentValidation/
├── ServiceCollectionExtensions.cs # DI 扩展
├── FluentValidationExtensions.cs # FluentValidation 扩展
├── ValidateResultFormatHandler.cs # 验证结果格式化处理器
└── Validators/
├── PhoneValidator.cs # 手机号验证器
├── IPValidator.cs # IP 验证器
└── UrlValidator.cs # URL 验证器
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 💡 配置说明
# appsettings.json 配置
{
"Validation": {
// 是否启用验证
"Enabled": true,
// 验证失败时返回详细错误
"DetailedErrors": true
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 配置项详解
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| Enabled | bool | true | 是否启用验证 |
| DetailedErrors | bool | true | 返回详细错误信息 |
# 🔧 创建验证器
# 1. 定义验证器类
文件位置: User.Web/Validators/UserAddModelValidator.cs
using FluentValidation;
using DarkM.Module.User.Application.UserService.ViewModels;
namespace DarkM.Module.User.Web.Validators
{
/// <summary>
/// 用户添加模型验证器
/// </summary>
public class UserAddModelValidator : AbstractValidator<UserAddModel>
{
public UserAddModelValidator()
{
// 用户名验证
RuleFor(x => x.Username)
.NotEmpty().WithMessage("用户名不能为空")
.Length(3, 20).WithMessage("用户名长度 3-20 位")
.Matches("^[a-zA-Z0-9_]+$").WithMessage("用户名只能包含字母、数字和下划线");
// 密码验证
RuleFor(x => x.Password)
.NotEmpty().WithMessage("密码不能为空")
.MinimumLength(6).WithMessage("密码长度至少 6 位");
// 邮箱验证
RuleFor(x => x.Email)
.EmailAddress().WithMessage("邮箱格式不正确")
.When(x => !string.IsNullOrEmpty(x.Email));
// 手机号验证
RuleFor(x => x.Phone)
.Matches("^1[3-9]\\d{9}$").WithMessage("手机号格式不正确")
.When(x => !string.IsNullOrEmpty(x.Phone));
}
}
}
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
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
# 2. 异步验证
public class UserAddModelValidator : AbstractValidator<UserAddModel>
{
private readonly IUserRepository _userRepository;
public UserAddModelValidator(IUserRepository userRepository)
{
_userRepository = userRepository;
RuleFor(x => x.Username)
.NotEmpty().WithMessage("用户名不能为空")
.MustAsync(BeUniqueUsername).WithMessage("用户名已存在");
}
private async Task<bool> BeUniqueUsername(
string username,
CancellationToken cancellationToken)
{
return !await _userRepository.ExistsAsync(username);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 3. 条件验证
RuleFor(x => x.Email)
.NotEmpty().WithMessage("邮箱不能为空")
.EmailAddress().WithMessage("邮箱格式不正确")
.When(x => x.ContactType == ContactType.Email);
RuleFor(x => x.Phone)
.NotEmpty().WithMessage("手机号不能为空")
.Matches("^1[3-9]\\d{9}$").WithMessage("手机号格式不正确")
.When(x => x.ContactType == ContactType.Phone);
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 4. 自定义验证
RuleFor(x => x.Password)
.NotEmpty().WithMessage("密码不能为空")
.Must(IsValidPassword).WithMessage("密码必须包含字母和数字");
private bool IsValidPassword(string password)
{
return password.Any(char.IsLetter) && password.Any(char.IsDigit);
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 💡 使用示例
# 1. 在 Controller 中手动验证
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using FluentValidation;
using DarkM.Module.User.Web.Validators;
using DarkM.Module.User.Application.UserService.ViewModels;
[Description("用户管理")]
public class UserController : ModuleController
{
private readonly IUserService _userService;
private readonly IValidator<UserAddModel> _validator;
public UserController(
IUserService userService,
IValidator<UserAddModel> validator)
{
_userService = userService;
_validator = validator;
}
[HttpPost]
[Description("添加用户")]
public async Task<IResultModel> Add(UserAddModel model)
{
// 手动验证
var result = await _validator.ValidateAsync(model);
if (!result.IsValid)
{
return ResultModel.Failed(result.Errors.First().ErrorMessage);
}
return await _userService.AddAsync(model);
}
}
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
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
# 2. 自动验证
DarkM 已集成自动验证,Controller 会自动验证模型:
[HttpPost]
[Description("添加用户")]
public async Task<IResultModel> Add(UserAddModel model)
{
// 模型已自动验证,无需手动调用
return await _userService.AddAsync(model);
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 3. 验证结果处理
var result = await _validator.ValidateAsync(model);
if (!result.IsValid)
{
// 获取所有错误
var errors = result.Errors.Select(e => e.ErrorMessage).ToList();
// 获取第一个错误
var firstError = result.Errors.First().ErrorMessage;
// 按字段分组错误
var errorsByField = result.Errors
.GroupBy(e => e.PropertyName)
.ToDictionary(
g => g.Key,
g => g.Select(e => e.ErrorMessage).ToList());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 🔧 内置验证规则
| 规则 | 说明 | 示例 |
|---|---|---|
| NotEmpty | 不能为空 | RuleFor(x => x.Name).NotEmpty() |
| NotNull | 不能为 null | RuleFor(x => x.Email).NotNull() |
| Length | 长度限制 | RuleFor(x => x.Name).Length(3, 20) |
| MinimumLength | 最小长度 | RuleFor(x => x.Pwd).MinimumLength(6) |
| MaximumLength | 最大长度 | RuleFor(x => x.Desc).MaximumLength(500) |
| EmailAddress | 邮箱格式 | RuleFor(x => x.Email).EmailAddress() |
| Matches | 正则匹配 | RuleFor(x => x.Phone).Matches("^1\\d{10}$") |
| InclusiveBetween | 范围 | RuleFor(x => x.Age).InclusiveBetween(18, 60) |
| Must | 自定义验证 | RuleFor(x => x.Name).Must(BeValid) |
| MustAsync | 异步自定义验证 | RuleFor(x => x.Name).MustAsync(BeUnique) |
# 🔧 验证规则组合
RuleFor(x => x.Password)
.NotEmpty().WithMessage("密码不能为空")
.MinimumLength(6).WithMessage("密码至少 6 位")
.MaximumLength(20).WithMessage("密码最多 20 位")
.Matches(".*[A-Z].*").WithMessage("密码必须包含大写字母")
.Matches(".*[a-z].*").WithMessage("密码必须包含小写字母")
.Matches(".*\\d.*").WithMessage("密码必须包含数字");
1
2
3
4
5
6
7
2
3
4
5
6
7
# 📚 最佳实践
# 1. 验证器命名
验证器类以 Validator 结尾:
public class UserAddModelValidator : AbstractValidator<UserAddModel>
public class OrderQueryModelValidator : AbstractValidator<OrderQueryModel>
1
2
2
# 2. 错误消息本地化
RuleFor(x => x.Username)
.NotEmpty().WithMessage(x => Localizer["UsernameRequired"])
.Length(3, 20).WithMessage(x => Localizer["UsernameLength"]);
1
2
3
2
3
# 3. 验证器复用
// 定义通用验证规则
public class EmailValidator : AbstractValidator<string>
{
public EmailValidator()
{
RuleFor(x => x)
.EmailAddress().WithMessage("邮箱格式不正确");
}
}
// 在其他验证器中使用
RuleFor(x => x.Email).SetValidator(new EmailValidator());
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 4. 级联验证
RuleFor(x => x.Address)
.NotNull()
.SetValidator(new AddressValidator());
public class AddressValidator : AbstractValidator<AddressModel>
{
public AddressValidator()
{
RuleFor(x => x.Province).NotEmpty();
RuleFor(x => x.City).NotEmpty();
RuleFor(x => x.Detail).NotEmpty();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 🔍 常见问题
# Q1: 验证器未生效?
检查清单:
- ✅ 是否正确实现了
AbstractValidator<T> - ✅ 验证器是否注册到容器
- ✅ 模型是否与验证器类型匹配
解决方案:
// 确保验证器在正确的命名空间
namespace DarkM.Module.User.Web.Validators
{
public class UserAddModelValidator : AbstractValidator<UserAddModel>
{
// ...
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# Q2: 异步验证不工作?
确保:
- ✅ 使用
MustAsync而不是Must - ✅ 使用
ValidateAsync而不是Validate
示例:
// ✅ 正确
RuleFor(x => x.Username).MustAsync(BeUniqueUsername);
var result = await _validator.ValidateAsync(model);
// ❌ 错误
RuleFor(x => x.Username).Must(BeUniqueUsername);
var result = _validator.Validate(model);
1
2
3
4
5
6
7
2
3
4
5
6
7
# Q3: 条件验证不生效?
检查 When 条件是否正确:
RuleFor(x => x.Email)
.NotEmpty()
.When(x => x.ContactType == ContactType.Email); // 条件
1
2
3
2
3
# Q4: 如何验证集合?
解决方案:
RuleForEach(x => x.Items)
.SetValidator(new ItemValidator());
public class ItemValidator : AbstractValidator<ItemModel>
{
public ItemValidator()
{
RuleFor(x => x.Name).NotEmpty();
RuleFor(x => x.Quantity).GreaterThan(0);
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# Q5: 如何验证嵌套对象?
解决方案:
RuleFor(x => x.Profile)
.NotNull()
.SetValidator(new ProfileValidator());
public class ProfileValidator : AbstractValidator<ProfileModel>
{
public ProfileValidator()
{
RuleFor(x => x.Bio).MaximumLength(500);
RuleFor(x => x.Avatar).EmailAddress();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 📚 相关文档
# 🔗 参考链接
- 源代码 (opens new window) -
src/Framework/Validation - FluentValidation 文档 (opens new window)
- FluentValidation GitHub (opens new window)
最后更新: 2024-09-20