MVC中的验证规则和业务规则

3
我是一名有用的助手,可以为您翻译文本。

我有一个MVC Web项目。根据最佳实践,添加验证规则和业务规则的正确位置在哪里?

验证规则将包括必填字段和所需格式。

业务规则将包括“此电子邮件已在数据库中使用”等内容。

以下是我目前在注册模型中的处理方式:

public class RegisterModel : IValidatableObject
{
    [Display(Name = "Email address")]
    [Required(ErrorMessage = "The email address is required")]
    [EmailAddress(ErrorMessage = "Invalid Email Address")]
    public string Email { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var retVal = new List<ValidationResult>();
        using (var entity = new AcademicUniteDatabaseEntities())
        {
            if (entity.UserProfiles.Any(x => x.UserName == this.Email))
            {
                retVal.Add(new ValidationResult("Email already exist", new List<string> { "Email" }));
            }
        }

        return retVal;
    }
}

这是我的注册控制器
    public ActionResult Register()
    {
        var model = new RegisterModel();
        return this.View(model);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Register(RegisterModel model)
    {
        if (!this.ModelState.IsValid)
        {
            return this.View(model);
        }

        model.CreateAccount();
        return this.View("WaitConfirmEmail");
    }

我为什么要这样做

  1. 当我在控制器中检查ModelState.IsValid时,它将会检查电子邮件的格式以及它是否已存在于数据库中。我不会在我的控制器中调用任何数据库,只在我的模型中调用。(这是最佳实践吗?)
  2. 它还将"电子邮件已存在"的验证结果绑定到我的电子邮件属性上,以便我可以在视图中显示验证结果。

这是最佳实践吗?

  1. 这是在MVC中添加业务规则的正确方式吗?
  2. 为什么或为什么不是?
  3. 如果这不是最佳实践,您能否提供一个例子,说明如何编写最佳的注册模型以检查业务规则(例如:电子邮件是否已存在)?
2个回答

3

使用注释来验证您的模型对于需要验证的小型应用程序是可以的,但是当您的验证规则变得越来越复杂并且您的模型开始变得更加复杂时,我建议看看像Fluent Validation这样的库。

通过属性验证模型的问题在于它可能会非常混乱,并且您将开始遭受很多重复;而使用Fluent Validation等库所获得的好处包括:

  • 允许您保持模型干净和简洁
  • 由于DI支持,创建更可维护和可重用的验证规则。
  • 不像ModelState.IsValid那样与UI绑定,因此可以在业务逻辑层等中重用。

典型的Fluent Validation规则示例:

public class CustomerValidator: AbstractValidator<Customer>
{
    public CustomerValidator()
    {
        RuleFor(customer => customer.Surname).NotEmpty();
        RuleFor(customer => customer.Forename).NotEmpty().WithMessage("Please specify a first name");
        RuleFor(customer => customer.Discount).NotEqual(0).When(customer => customer.HasDiscount);
        RuleFor(customer => customer.Address).Length(20, 250);
        RuleFor(customer => customer.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
    }

    private bool BeAValidPostcode(string postcode)
    {
        // custom postcode validating logic goes here
    }
}

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
ValidationResult results = validator.Validate(customer);

bool validationSucceeded = results.IsValid;
IList<ValidationFailure> failures = results.Errors;

谢谢您的回答,我会查看流畅验证。我的一个担忧是检查电子邮件是否存在更多地涉及业务逻辑(必须访问数据库),而不是检查属性的长度(验证)。所以我想知道是否应该以同样的方式验证业务逻辑,或者这应该在单独的API或服务中完成。 - Jacob Heck

0

一个选择是创建自定义的验证器属性。使用你的解决方案,你可能会得到非常庞大的模型。另一个选择是创建静态帮助程序,你可以将其插入控制器中。这里没有正确答案,只是您想组织代码的方式。我更喜欢在自定义属性中分散业务逻辑,这样您就可以轻松地进行重构。


谢谢你的回答。我想我的担忧是,检查电子邮件是否存在的代码,应该放在单独的API或服务中,而不是将逻辑编码在验证函数中。 - Jacob Heck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接