6

このプロパティをMVCモデルで検証しようとしています。このモデルには、コンマで区切られた0個以上の電子メールアドレスを含めることができます。

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()を含む最も近い解決策は、以下のカスタムValidatorを作成し、文字列から各電子メールでValidateを呼び出すことでしたが、何らかの理由で機能しませんでした(AbuseEmailValidatorは述語x=>x-validatorを呼び出すとき。各電子メールで検証します)。

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

これを簡単な方法で行う方法はありますか?このソリューションに似ていますが、SetCollectionValidatorを使用できないため、文字列のリストではなく1つの文字列を使用します(または使用できますか?):Fluent Validationを使用して、リスト内の各文字列に対してどのように検証しますか?

4

5 に答える 5

8

あなたはこのようなことを試すことができます:

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を使用すればうまくいくようです。私のメールは「;」で区切られています この場合。それが役に立てば幸い。

于 2014-07-01T12:30:48.610 に答える
2

バージョン9以降、FluentValidationは、TransformおよびForEachメソッドを使用するカスタムバリデーターを必要とせずにこれをサポートします。

バージョン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());

Transformnullを処理するには、デリゲートでnull合体演算子を使用します。

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

空白を処理するには、リスト内の各項目をトリミングすることをお勧めします。Select次の句を追加できます。

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

空のアイテムを無視する場合は、次のWhere句を追加します。

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

リストに少なくとも1つの項目があることを要求するには、NotEmptyルールを追加します。したがって、最終バージョン9.5以降のコードは次のようになります。

Transform(x => x.List, 
    list => (list ?? "").Split(',')
        .Select(item => item.Trim())
        .Where(item => !string.IsNullOrEmpty(item)))
    .NotEmpty()
    .ForEach(itemRule => itemRule.EmailAddress());
于 2021-08-18T21:00:33.957 に答える
1

上記の回答は良いですが、かなり古いものです。そのため、一部のコードは、FluentValidationNugetパッケージのバージョンでは機能しません。少なくともビルドエラーが発生しました。また、ソリューションはより洗練されたものにすることができます。これを使用することをお勧めします:

モデル:

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.");
        }
    }
于 2020-02-10T21:55:11.203 に答える
0

カスタムバリデーター拡張機能を作成できます。このようにして、必要なセパレータを定義し、特定のプロパティだけでなくすべての文字列プロパティに使用し、条件に基づいて異なるメッセージを追加できます。

カスタムバリデーターの詳細については、ドキュメントをご覧ください: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);
    }
}
于 2021-01-20T11:47:28.877 に答える
0

.When(), .Unless()もう少しシンプルで、やのような条件でチェーンできるようにしたかったのです.WithMessage()。だから私は拡張メソッドを使ってBurhanSavciのソリューションに基づいて構築しました:

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");
        }
    }
于 2021-03-12T08:36:11.073 に答える