在FluentValidation中为RuleSet设置消息失败

时间:2013-05-27 14:14:04

标签: c# validation fluentvalidation

FluentValidation中有可能为RuleSet提供特定的错误消息吗?

类似的东西:

RuleSet("LoginInformation", () =>
{
    RuleFor(m => m.Email).NotEmpty().EmailAddress();
    RuleFor(m => m.Password).NotEmpty();
}); // I thought I can add a WithMessage here...

如果任何规则失败,则应显示单个错误消息。

1 个答案:

答案 0 :(得分:1)

简单解决方案

在不更改FluentValidation源的情况下,无法直接将错误消息设置为RuleSet,但您可以在特殊列表中收集规则,并在循环中设置相同的错误消息:

RuleSet("LoginInformation", () =>
{
    var loginRules = new List<IRuleBuilderOptions<CredentialsModel, object>>
    {
        RuleFor(m => m.Email).NotEmpty().EmailAddress(),
        RuleFor(m => m.Password).NotEmpty()
    };

    foreach (var rule in rules)
    {
        // this overload chosen just to show, that all possibilities to set error message still available
        rule.WithMessage("An error occured. Additional information: {0}, {1}", (model, value) => value, (model, value) => model.Email);
    }
});

只有2 cascade mode options,你无法告诉验证者在第一次失败时停止所有规则,所以你只需要从验证结果中取出第一个错误并删除其他相同的错误。为此,您可以覆盖AbstractValidator

public class SingleErrorValidator<T> : AbstractValidator<T>
{
    public override ValidationResult Validate(ValidationContext<T> context)
    {
        var result = base.Validate(context);

        if (result.IsValid)
            return result;

        var singleErrorList = new List<ValidationFailure> { result.Errors.First() };
        var singleErrorResult = new ValidationResult(singleErrorList);

        return singleErrorResult;
    }
}

高级解决方案:

如果您分叉了FluentValidation并且可以修改解决方案中的源代码,那么您可以为常见错误消息制作可重用的方法,并在RuleSet类中添加下一个AbstractValidator方法重载(您不能使用扩展方法或继承,因为重要字段具有私有修饰符):

public void RuleSet(string ruleSetName, Func<List<IRuleBuilderOptions<T, object>>> function, Action<IRuleBuilderOptions<T, object>> writeRuleMessage)
{
    ruleSetName.Guard("A name must be specified when calling RuleSet.");
    function.Guard("A ruleset definition must be specified when calling RuleSet.");

    using (nestedValidators.OnItemAdded(r => r.RuleSet = ruleSetName))
    {
        var list = function();

        foreach (var rule in list)
        {
            writeRuleMessage(rule);
        }
    }
}

然后您可以使用RuleSet下一个方式:

RuleSet("LoginInformation", () =>
{
    var rules = new List<IRuleBuilderOptions<B, object>>()
    {
        RuleFor(x => x.Name).NotEmpty().EmailAddress(),
        RuleFor(x => x.Email).NotEmpty()
    };

    return rules;
}, (rule) => 
{ 
    rule.WithMessage("An error occured");
}); // still can use another overloads, that allow access to model and validated properties

<强>结论

两种解决方案都能够正常工作,但由于KISS原则,我建议使用1-st。