使用 FluentValidation 进行更深入的数据库验证是不好的做法吗?

问题描述 投票:0回答:2

例如,您可以验证电话号码或电子邮件,这些都在 FV 文档中,但如果您需要确保该电话号码尚未被经理“X”的员工使用怎么办?我知道可以通过所有自定义验证器选项来做到这一点。但我应该在那里做吗?

如果我将这个逻辑放入一个复杂的自定义 FluentValidation 中,我担心我在错误的位置编写了这个逻辑。传统上,我只是将其添加到我通常会进行添加/保存的区域的服务中。

由于我已经为我的视图模型设置了流畅的验证,并且我正在那里进行其他类型的验证,因此将所有内容都放在一个地方似乎很诱人。然而,这意味着如果我在非 Web 应用程序中重用我的逻辑,我必须记住以其他方式执行这些验证器,而不是通过 modelbinder 实现自动化

IsValid()
。尽管我没有花费额外的时间编码来使我的项目变得模块化。我离题了。

asp.net-web-api asp.net-core fluentvalidation
2个回答
9
投票

嗯,不,这实际上是一个好地方。无论底层框架如何,您都希望将验证保持在同一位置并使其可重用于您的逻辑。

在 ASP.NET 中使用属性验证和 ModelStateDictionary 将您的逻辑绑定到 ASP.NET,而 FluentValidation 可以用于所有内容。您仍然可以使用服务来包装该逻辑,并且仅在验证成功时添加/更新实体。我大量使用 fluidvalidation,即使是在我需要进行数据库查询的情况下,它也能正常工作。我可以简单地从 ASP.NET 切换到控制台应用程序或其他应用程序,并且两者具有完全相同的逻辑。

将验证逻辑分为几个区域听起来不像最佳实践。


1
投票

使用

FluentValidation
进行 Web 请求验证时,了解简单验证和需要数据库访问的验证之间的界限非常重要。你不应该将两者混合在一起。验证不是数据验证。验证只是在任何数据库访问或数据交互之前检查对象的有效性。在验证请求对象之前访问数据库没有什么意义!想想吧。

在哪里使用
FluentValidation

基本验证:使用

FluentValidation
验证无需访问数据库即可检查的属性。这包括:

  • 必填字段
  • 字符串长度
  • 模式(例如电子邮件格式)
  • 范围检查(例如,年龄在 18 到 99 之间)
  • 不需要数据库访问的自定义逻辑

在哪里使用数据库验证

  • 唯一性检查:当您需要确保值是唯一的(例如用户名、电子邮件)时,您可能需要查询数据库。

  • 存在检查:当验证相关实体是否存在(例如外键关系)时,需要进行数据库查询。

  • 复杂的业务规则:某些业务规则可能需要查询数据库以确保满足它们。

要彻底分离这些问题,请使用 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();
    }
}

您可以保持关注点的清晰分离,确保您的验证在内存中以及必要时通过数据库访问得到适当处理。

© www.soinside.com 2019 - 2024. All rights reserved.