【问题标题】:Validating against a string containing comma delimited emails验证包含逗号分隔电子邮件的字符串
【发布时间】:2012-09-07 14:23:52
【问题描述】:

我正在尝试在 MVC 模型中验证此属性,它可以包含零个或多个用逗号分隔的电子邮件地址:

public class DashboardVM
{
    public string CurrentAbuseEmails { get; set; }
    ...
}

问题是如何使用电子邮件地址的内置流利验证规则来做到这一点? 现在我有一个使用 Must 和正则表达式的解决方案,但我觉得它不够优雅。

    public DashboardVMValidator()
    {
        RuleFor(x => x.CurrentAbuseEmails).Must(BeValidDelimitedEmailList).WithMessage("One or more email addresses are not valid.");
    }

    private bool BeValidDelimitedEmailList(string delimitedEmails)
    {
        //... match very very long reg. expression
    }

到目前为止,包括 RuleFor(...).EmailAddress() 在内的最接近的解决方案是在下面创建一个自定义验证器,并从字符串中对每封电子邮件调用 Validate,但由于某种原因这不起作用(AbuseEmailValidator 无法得到我的谓词 x => x - 在每封电子邮件上调用 validator.Validate 时)。

public class AbuseEmailValidator : AbstractValidator<string>
{
    public AbuseEmailValidator()
    {
        RuleFor(x => x).EmailAddress().WithMessage("Email address is not valid");
    }
}

有没有办法以某种简单的方式做到这一点?类似于此解决方案的东西,但使用一个字符串而不是字符串列表,因为我不能使用 SetCollectionValidator(或者我可以吗?):How do you validate against each string in a list using Fluent Validation?

【问题讨论】:

    标签: c# asp.net-mvc-3 validation fluentvalidation


    【解决方案1】:

    你可以试试这样的:

    public class InvoiceValidator : AbstractValidator<ContractInvoicingEditModel>
    {
        public InvoiceValidator()
        {
            RuleFor(m => m.EmailAddressTo)
                .Must(CommonValidators.CheckValidEmails).WithMessage("Some of the emails   provided are not valid");
        }
    }
    
    public static class CommonValidators
    {
        public static bool CheckValidEmails(string arg)
        {
            var list = arg.Split(';');
            var isValid = true;
            var emailValidator = new EmailValidator();
    
            foreach (var t in list)
            {
                isValid = emailValidator.Validate(new EmailModel { Email = t.Trim() }).IsValid;
                if (!isValid)
                    break;
            }
    
            return isValid;
        }
    }
    public class EmailValidator : AbstractValidator<EmailModel>
    {
        public EmailValidator()
        {
            RuleFor(x => x.Email).EmailAddress();
        }
    }
    
    public class EmailModel
    {
        public string Email { get; set; }
    }
    

    如果您使用中介 poco,它似乎可以正常工作。我的电子邮件由“;”分隔在这种情况下。希望对您有所帮助。

    【讨论】:

      【解决方案2】:

      从版本 9 开始,FluentValidation 支持此功能,无需使用 TransformForEach 方法的自定义验证器。

      在 9.0-9.4 版本中,你可以这样写:

      RuleFor(x => x.List)
          .Transform(list => list.Split(','))
          .ForEach(itemRule => itemRule.EmailAddress());
      

      在 9.5 及更高版本中,RuleFor 不与 Transform 一起使用,因此您可以这样编写:

      Transform(x => x.List, list => list.Split(','))
          .ForEach(itemRule => itemRule.EmailAddress());
      

      要处理空值,请在Transform 委托中使用空合并运算符:

      list => (list ?? "").Split(',')
      

      要处理空格,您可能需要修剪列表中的每个项目。您可以添加Select 子句:

      list => (list ?? "").Split(',')
          .Select(item => item.Trim())
      

      如果要忽略空项,请添加Where 子句:

      list => (list ?? "").Split(',')
          .Select(item => item.Trim())
          .Where(item => !string.IsNullOrEmpty(item))
      

      要要求列表中至少有一项,请添加NotEmpty 规则。所以最终版本 9.5+ 的代码将如下所示:

      Transform(x => x.List, 
          list => (list ?? "").Split(',')
              .Select(item => item.Trim())
              .Where(item => !string.IsNullOrEmpty(item)))
          .NotEmpty()
          .ForEach(itemRule => itemRule.EmailAddress());
      

      【讨论】:

        【解决方案3】:

        上面提供的答案很好,但很旧。因此,某些代码不适用于 FluentValidation Nuget 包的非版本。至少我得到了构建错误。解决方案也可以更复杂。 推荐使用这个:

        型号:

        public sealed class Email
        {
            public string From { get; set; }
        
            /// <summary>
            /// Email address(es) to (can be settable separated list, default: ;)
            /// </summary>
            public string To { get; set; }
        
            //.....
        
            /// <summary>
            /// Separator char for multiple email addresses
            /// </summary>
            public char EmailAddressSeparator { get; set; }
        
            public Email()
            {
                EmailAddressSeparator = ';';
            }
        }
        

        自定义验证器:

        public static class CommonValidators
        {
            public static bool CheckValidEmails(Email email, string emails)
            {
                if(string.IsNullOrWhiteSpace(emails))
                {
                    return true;
                }
        
                var list = emails.Split(email.EmailAddressSeparator);
                var isValid = true;
        
                foreach (var t in list)
                {
                    var email = new EmailModel { Email = t.Trim() };
                    var validator = new EmailModelValidator();
        
                    isValid = validator.Validate(email).IsValid;
                    if (!isValid)
                    {
                        break;
                    }
                }
        
                return isValid;
            }
        
            private class EmailModel
            {
                public string Email { get; set; }
            }
            private class EmailModelValidator : AbstractValidator<EmailModel>
            {
                public EmailModelValidator()
                {
                    RuleFor(x => x.Email).EmailAddress(EmailValidationMode.AspNetCoreCompatible).When(x => !string.IsNullOrWhiteSpace(x.Email));
                }
            }
        }
        

        用法:

            public class EmailValidator : AbstractValidator<Email>
            {
                public EmailValidator()
                {
                    RuleFor(x => x.To).NotEmpty()
                        .Must(CommonValidators.CheckValidEmails)
                        .WithMessage($"'{nameof(To)}' some of the emails provided are not a valid email address.");
                }
            }
        

        【讨论】:

          【解决方案4】:

          您可以编写自定义验证器扩展。 通过这种方式,您可以定义任何您想要的分隔符,将它用于每个字符串属性,而不仅仅是特定属性,并根据条件添加不同的消息。

          您可以从文档中了解有关自定义验证器的更多信息:https://docs.fluentvalidation.net/en/latest/custom-validators.html

          自定义验证器扩展:

          public static class ValidatorExtensions
          {
              public static IRuleBuilderInitial<T, string> CheckValidEmails<T>(this IRuleBuilder<T, string> ruleBuilder, string separator)
              {
                  bool isValid;
                  var emailValidator = new EmailValidator();
                  return ruleBuilder.Custom((emailsStr, context) =>
                  {
                      if (string.IsNullOrWhiteSpace(emailsStr))
                      {
                          context.AddFailure($"'{context.DisplayName}' must not be empty");
                          return;
                      }
          
                      var emails = emailsStr.Split(separator);
                      foreach (var email in emails)
                      {
                          isValid = emailValidator.Validate(email.Trim()).IsValid;
                          if (!isValid)
                          {
                              context.AddFailure($"'{email}' is not a valid email address");
                              break;
                          }
                      }
                  });
              }
          
              private class EmailValidator : AbstractValidator<string>
              {
                  public EmailValidator()
                  {
                      RuleFor(x => x).EmailAddress();
                  }
              }
          }
                 
          

          如果您希望分隔符作为模型属性,那么您可以这样编写扩展:

          public static IRuleBuilderInitial<T, string> CheckValidEmails<T>(this IRuleBuilder<T, string> ruleBuilder, Func<T, string> separatorSelector)
              {
                  if (separatorSelector == null)
                      throw new ArgumentNullException(nameof(separatorSelector), $"{nameof(separatorSelector)} cannot be null");
                  
                  bool isValid;
                  var emailValidator = new EmailValidator();
                  return ruleBuilder.Custom((emailsStr, context) =>
                  {
                      if (string.IsNullOrWhiteSpace(emailsStr))
                      {
                          context.AddFailure($"'{context.DisplayName}' must not be empty");
                          return;
                      }
          
                      var separator = separatorSelector.Invoke((T) context.InstanceToValidate);
                      var emails = emailsStr.Split(separator);
                      foreach (var email in emails)
                      {
                          isValid = emailValidator.Validate(email.Trim()).IsValid;
                          if (!isValid)
                          {
                              context.AddFailure($"'{email}' is not a valid email address");
                              break;
                          }
                      }
                  });
              }
          
                                                               
          

          示例模型:

          public class EmailsModel
          {
          
              /// <summary>
              /// emails separated by ;
              /// </summary>
              public string Emails { get; set; }
          
              public string EmailsSeparator { get; set; } = ";";
          }
          

          用法:

          public class EmailsModelValidator : AbstractValidator<EmailsModel>
          {
              public EmailsModelValidator()
              {
                  RuleFor(x => x.Emails).CheckValidEmails(";");
                  RuleFor(x => x.Emails).CheckValidEmails(x => x.EmailsSeparator);
              }
          }
          

          【讨论】:

            【解决方案5】:

            我想要一些更简单的东西,并且能够与.When(), .Unless().WithMessage() 等条件进行链接。所以我在Burhan Savcis 解决方案的基础上使用了扩展方法:

            public static class ValidatorExtensions
            {
                public static IRuleBuilderOptions<T, string> CheckValidEmails<T>(this IRuleBuilder<T, string> ruleBuilder, string separator)
                {
                    var emailValidator = new EmailValidator();
            
                    return ruleBuilder.Must(emails => emails.Split(separator).All(email => emailValidator.Validate(email.Trim()).IsValid));
                }
            
                private class EmailValidator : AbstractValidator<string>
                {
                    public EmailValidator()
                    {
                        RuleFor(x => x).EmailAddress();
                    }
                }
            }
            

            在我的例子中,我有一个用于导出数据的 CRQS 命令,其中包含更多输入选项,包括选择导出类型(文件/电子邮件/其他选项)的下拉菜单。

                public class Command : IRequest<Result>
                {
                    public string EmailAddress{ get; set; }
                    public ExportType ExportType{ get; set; }
            
                }
            

            然后像这样使用它:

                public class Validator : AbstractValidator<Command>
                {
                    public Validator()
                    {
                        RuleFor(c => c.ExportOptions.EmailAddress).CheckValidEmails(",").When(c => c.ExportType == ExportType.Email).WithMessage("One or more email addresses are not valid");
                    }
                }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-06-10
              • 2011-01-22
              • 2013-10-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-09-15
              • 2013-05-31
              相关资源
              最近更新 更多