FluentValidation 中每个自定义规则的客户端验证的最佳方法

Best approach for Client-side validation for each custom rule in FluentValidation

我在 Asp.Net MVC 4 应用程序中使用 FluentValidation。我已经知道一些规则会自动为 jQuery 验证库生成属性。并且这个脚本库已经知道它必须检查什么,例如 data-rule-requireddata-rule-range 等等。

我知道 FluentValidation 中有一些函数,但这些函数不包含在客户端。例如:.Equal(true)。 我已经检查了 @DarinDimitrov answer here 并毫无问题地实施了它。

但是,我不想总是创建继承自 FluentValidationPropertyValidator 的新 class。我们必须像 global.asax:

那样将其添加到提供程序中
provider.Add(typeof(EqualValidator), (metadata, context, description, validator) => new EqualToValueClientRule(metadata, context, description, validator));

在这种情况下 EqualValidator 已经在 FluentValidation 中实施。但是,如果我们创建了一个带有 When 关键字的验证器呢?例如,我有:

this.RuleFor(phone => phone.Digits)
    .Length(7)
        .When(phone => phone.PrefixId == 2)
        .WithMessage("Numbers in 2nd city must contain 7 characters");

this.RuleFor(phone => phone.Digits)
    .Length(7)
        .When(phone => phone.PrefixId > 64)
        .WithMessage("Mobile number must contain 7 characters");

this.RuleFor(phone => phone.Digits)
    .Length(5)
        .When(phone => phone.PrefixId != 2)
        .WithMessage("Numbers in other cities must contain 5 characters")

当然,我可以用jQuery/JavaScript检查这个没有任何问题。但是,这种做法并不好。在其他情况下,您必须编写大量代码才能在客户端生成自定义属性并向适配器添加新功能。或者,只使用 jQuery/JavaScript?或者其他什么?也许我们可以将 JavaScript 函数名称添加到 FluentValidationPropertyValidator?

你有什么推荐给我的?

我想了很多,发现最好的方法是创建新的验证器,它继承自 PropertyValidator 并实现 IClientValidatable 接口。因此,它将包含服务器端验证,并将按照我们的意愿生成不显眼的属性。然后我们必须需要在不显眼的库中注册这个新的验证器。

例如,我问题中规则的验证器将是:

public class MustFitToPhonePrefix<TModel, TProperty> : PropertyValidator, IClientValidatable
    {
        private string dependencyElement;

        public MustFitToPhonePrefix(Expression<Func<TModel, TProperty>> expression)
            : base("Format is wrong")
        {
            dependencyElement = (expression.Body as MemberExpression).Member.Name;
        }

        // Server side validation
        protected override bool IsValid(PropertyValidatorContext context)
        {
            // Instance of the class which contains property which must be validated 
            var phone = context.ParentContext.InstanceToValidate as PhoneDetail;

            ...
            // Custom logic
            ...

            // Everything is valid
            return true;
        }

        // Generate jquery unobtrusive attributes
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = this.ErrorMessageSource.GetString(), // default error message
                ValidationType = "fittoprefix" // name of the validatoin which will be used inside unobtrusive library
            };

            rule.ValidationParameters["prefixelement"] = dependencyElement; // html element which includes prefix information
            yield return rule;
        }

现在我们可以注册客户端验证器了:

// Will check if the phone number fits to phone prefix
$.validator.addMethod('fittoprefix', function (value, element, params) {
    var parent = getParentPropertyName(element);
    var prefixId = $("#{0}_{1}".format(parent, params.prefixelement)).val();
    var digitsLength = $(element).val().Length;

    ...
    // Custom logic
    ...

    return true;
});

// Registration - Will check if the phone number fits to phone prefix
$.validator.unobtrusive.adapters.add('fittoprefix', ['prefixelement'], function (options) {
    options.rules['fittoprefix'] = options.params;
    if (options.message != null) {
        options.messages['fittoprefix'] = options.message;
    }
});

最后,我们可以设置我们的验证器:

   this.RuleFor(m => m.Digits)
       .SetValidator(new MustFitToPhonePrefix<PhoneDetail, int>(m => m.PrefixId));