代码之家  ›  专栏  ›  技术社区  ›  MB34

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

  •  0
  • MB34  · 技术社区  · 7 年前

    {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 回复  |  直到 7 年前
        1
  •  0
  •   MB34    7 年前

    这是杰里米·斯金纳的回答。。。

    这种行为是正常和正确的。复杂子验证程序集 with SetValidator只能在目标属性不可用时调用 无效的由于To属性为null,因此子验证器将不可用 调用。您应该做的是将SetValidator与

    RuleFor(model=>model.To).NotNull().WithMessage(“…”);

    …然后从中删除RuleFor(model=>model).NotNull() EmailAddressListAtLeastOneRequiredValidator,因为这将永远不会发生 执行。覆盖PreValidate在此处不相关。使用 像这样的PreValidate仅在尝试将null传递给 根验证程序。当使用子验证器时,它们永远不能 可以使用null实例调用(这是通过设计实现的),并且 应该由父验证器处理。