FluentValidation无法正确验证电子邮件地址列表?

时间:2018-12-20 17:53:11

标签: fluentvalidation

{Version 8.0.0}

为什么这个测试会通过?

测试:

[Test]
public void Validation_NullTo_ShouldThrowModelValidationException()
{
    var config = new EmailMessage
    {
        Subject = "My Subject",
        Body = "My Body",
        To = null
    };

    EmailMessageValidator validator = new EmailMessageValidator();

    ValidationResult results = validator.Validate(config);
    if (!results.IsValid)
    {
        foreach (var failure in results.Errors)
        {
            Console.WriteLine("Property " + failure.PropertyName + " failed validation. Error was: " + failure.ErrorMessage);
        }
    }
    // results.Errors is Empty and resuts.IsValid is true here. Should be false with at least one Error message.
    Assert.True(results.IsValid);
}    

如果我这样更改消息的结构,验证通常会失败。

var config = new EmailMessage
{
    Subject = "My Subject",
    Body = "My Body"                
};
config.To = new EmailAddressList();

验证者:

public class EmailAddressListAtLeastOneRequiredValidator : AbstractValidator<EmailAddressList>
{
    public EmailAddressListAtLeastOneRequiredValidator()
    {
        RuleFor(model => model)
            .NotNull()
            .WithMessage(AppMessages.Validation.AtLeastOneShouldBeDefined.ParseIn(nameof(EmailAddressList.Value)));

        RuleFor(model => model.Value)
            .NotNull()
            .WithMessage(AppMessages.Validation.AtLeastOneShouldBeDefined.ParseIn(nameof(EmailAddressList.Value)));

        RuleFor(model => model.Value.Count)
            .GreaterThan(0)
            .WithMessage(AppMessages.Validation.AtLeastOneShouldBeDefined.ParseIn(nameof(EmailAddressList.Value)));

        When(model => model.Value?.Count > 0, () =>
        {
            RuleFor(model => model.Value)
                .NotEmpty()
                .WithMessage(AppMessages.Validation.AtLeastOneShouldBeDefined.ParseIn(nameof(EmailAddressList.Value)));
        });
    }
}

public class EmailAddressListValidator : AbstractValidator<EmailAddressList>
{
    public EmailAddressListValidator()
    {
        RuleFor(model => model.Value).SetCollectionValidator(new EmailAddressValidator());
    }
}

public class EmailAddressValidator : AbstractValidator<EmailAddress>
{
    public EmailAddressValidator()
    {
        When(model => model.Value != null, () =>
        {
            RuleFor(model => model.Value)
                .EmailAddress()
                .WithMessage(AppMessages.Validation.ValueCannotBeNullOrEmpty.ParseIn(nameof(EmailAddress)));
        });
    }
}

public class EmailMessageValidator : AbstractValidator<EmailMessage>
{
    public EmailMessageValidator()
    {
        RuleFor(model => model.To).SetValidator(new EmailAddressListAtLeastOneRequiredValidator());

        When(model => model.Cc?.Value?.Count > 0, () =>
        {
            RuleFor(model => model.Cc).SetValidator(new EmailAddressListValidator());
        });

        When(model => model.Bcc?.Value?.Count > 0, () =>
        {
            RuleFor(model => model.Bcc).SetValidator(new EmailAddressListValidator());
        });

        RuleFor(model => model.Subject)
            .NotEmpty().WithMessage(AppMessages.Validation.ValueCannotBeNullOrEmpty.ParseIn(nameof(EmailMessage.Subject)))
            .MaximumLength(100).WithMessage(AppMessages.Validation.ValueLengthCannotBeGreaterThan.ParseIn(nameof(EmailMessage.Subject), 100));

        RuleFor(model => model.Body)
            .NotEmpty().WithMessage(AppMessages.Validation.ValueCannotBeNullOrEmpty.ParseIn(nameof(EmailMessage.Body)));
    }
}

EmailMessage和EmailAddressList类:

public class EmailMessage : IEmailMessage
{
    public EmailAddressList To { get; set; } = new EmailAddressList();
    public EmailAddressList Cc { get; set; } = new EmailAddressList();
    public EmailAddressList Bcc { get; set; } = new EmailAddressList();
    public string Subject { get; set; }
    public string Body { get; set; }
}


public class EmailAddressList : ModelValidation, IEnumerable<EmailAddress>
{
    public List<EmailAddress> Value { get; set; } = new List<EmailAddress>();

    public EmailAddressList()
        : base(new EmailAddressListValidator())
    {

    }

    public EmailAddressList(string emailAddressList)
    : base(new EmailAddressListValidator())
    {
        Value = Split(emailAddressList);
    }

    public EmailAddressList(IValidator validator)
        : base(validator ?? new EmailAddressListValidator())
    {

    }

    public List<EmailAddress> Split(string emailAddressList, char splitChar = ';')
    {
        return emailAddressList.Contains(splitChar)
            ? emailAddressList.Split(splitChar).Select(email => new EmailAddress(email)).ToList()
            : new List<EmailAddress> { new EmailAddress(emailAddressList) };
    }

    public string ToString(char splitChar = ';')
    {
        if (Value == null)
            return "";

        var value = new StringBuilder();
        foreach (var item in Value)
            value.Append($"{item.Value};");

        return value.ToString().TrimEnd(';');
    }

    public void Add(string emailAddress, string displayName = "")
    {
        Value.Add(new EmailAddress(emailAddress, displayName));
    }

    public void Add(EmailAddress emailAddress)
    {
        Value.Add(emailAddress);
    }

    public IEnumerator<EmailAddress> GetEnumerator()
    {
        return Value.GetEnumerator();
    }

    [ExcludeFromCodeCoverage]
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

1 个答案:

答案 0 :(得分:0)

这是杰里米·斯金纳的答案...

  

此行为是正常且正确的。复杂的儿童验证器集   仅当目标属性不是   空值。由于To属性为null,因此子验证器不会   调用。您应该做的是将SetValidator与   NotNull检查:

     

RuleFor(model => model.To).NotNull()。WithMessage(“ ...”);
  .SetValidator(new EmailAddressListAtLeastOneRequiredValidator());

     

...然后从中删除RuleFor(model => model).NotNull()   EmailAddressListAtLeastOneRequiredValidator,因为它将永远不会   被执行。覆盖PreValidate与此处无关。使用   这样的PreValidate仅在尝试将null传递给   根验证器。与儿童验证器一起工作时,他们永远不能   使用null实例(这是设计使然)调用的,并且null   应该由父验证器处理。