例如,您可以验证电话号码或电子邮件,这些都在 FV 文档中,但如果您需要确保该电话号码尚未被经理“X”的员工使用怎么办?我知道可以通过所有自定义验证器选项来做到这一点。但我应该在那里做吗?
如果我将这个逻辑放入一个复杂的自定义 FluentValidation 中,我担心我在错误的位置编写了这个逻辑。传统上,我只是将其添加到我通常会进行添加/保存的区域的服务中。
由于我已经为我的视图模型设置了流畅的验证,并且我正在那里进行其他类型的验证,因此将所有内容都放在一个地方似乎很诱人。然而,这意味着如果我在非 Web 应用程序中重用我的逻辑,我必须记住以其他方式执行这些验证器,而不是通过 modelbinder 实现自动化
IsValid()
。尽管我没有花费额外的时间编码来使我的项目变得模块化。我离题了。
嗯,不,这实际上是一个好地方。无论底层框架如何,您都希望将验证保持在同一位置并使其可重用于您的逻辑。
在 ASP.NET 中使用属性验证和 ModelStateDictionary 将您的逻辑绑定到 ASP.NET,而 FluentValidation 可以用于所有内容。您仍然可以使用服务来包装该逻辑,并且仅在验证成功时添加/更新实体。我大量使用 fluidvalidation,即使是在我需要进行数据库查询的情况下,它也能正常工作。我可以简单地从 ASP.NET 切换到控制台应用程序或其他应用程序,并且两者具有完全相同的逻辑。
将验证逻辑分为几个区域听起来不像最佳实践。
使用
FluentValidation
进行 Web 请求验证时,了解简单验证和需要数据库访问的验证之间的界限非常重要。你不应该将两者混合在一起。验证不是数据验证。验证只是在任何数据库访问或数据交互之前检查对象的有效性。在验证请求对象之前访问数据库没有什么意义!想想吧。
FluentValidation
基本验证:使用
FluentValidation
验证无需访问数据库即可检查的属性。这包括:
唯一性检查:当您需要确保值是唯一的(例如用户名、电子邮件)时,您可能需要查询数据库。
存在检查:当验证相关实体是否存在(例如外键关系)时,需要进行数据库查询。
复杂的业务规则:某些业务规则可能需要查询数据库以确保满足它们。
要彻底分离这些问题,请使用 FluentValidation 进行基本检查,并在服务层或自定义验证器中执行数据库检查。
您可以创建包含数据库检查的自定义验证器
public class UniqueEmailValidator : AbstractValidator<UserModel>
{
private readonly IUserRepository _userRepository;
public UniqueEmailValidator(IUserRepository userRepository)
{
_userRepository = userRepository;
RuleFor(x => x.Email)
.NotEmpty()
.EmailAddress()
.MustAsync(BeUniqueEmail).WithMessage("Email already exists.");
}
private async Task<bool> BeUniqueEmail(string email, CancellationToken cancellationToken)
{
return !(await _userRepository.EmailExistsAsync(email, cancellationToken));
}
}
确保您的自定义验证器已注册到依赖项注入容器:
services.AddTransient<IValidator<UserModel>, UniqueEmailValidator>();
对于更复杂的验证逻辑,请考虑在服务层中处理这些检查
public class UserService
{
private readonly IUserRepository _userRepository;
private readonly IValidator<UserModel> _validator;
public UserService(IUserRepository userRepository, IValidator<UserModel> validator)
{
_userRepository = userRepository;
_validator = validator;
}
public async Task<Result> CreateUserAsync(UserModel model)
{
var validationResult = await _validator.ValidateAsync(model);
if (!validationResult.IsValid)
{
return Result.Failure(validationResult.Errors);
}
// Perform database validations
if (await _userRepository.EmailExistsAsync(model.Email))
{
return Result.Failure("Email already exists.");
}
// Proceed with user creation
await _userRepository.AddUserAsync(model);
return Result.Success();
}
}
您可以保持关注点的清晰分离,确保您的验证在内存中以及必要时通过数据库访问得到适当处理。