【问题标题】:ASP.NET MVC custom multiple fields validationASP.NET MVC 自定义多字段验证
【发布时间】:2017-06-21 07:49:37
【问题描述】:

我正在开发一个 ASP.NET MVC 5.2.3 自定义数据注释,用于在 Visual Studio 2015 中进行验证。它需要采用任意数量的字段并确保如果一个有值,它们都必须有一个值;如果它们都是空/空白,那应该没问题。

一些例子有所帮助:

但是,我不确定在验证未知数量的字段时如何进行客户端验证。

如何使用IClientValidatable 接口的GetClientValidationRules() 方法的实现将其传递给客户端?

另外,如何将这个新的数据注释应用到我的视图模型上的属性?会是这样吗?

[MultipleRequired("AppNumber", "UserId", /* more fields */), ErrorMessage = "Something..."]
[DisplayName("App #")]
public int AppNumber { get; set; }

[DisplayName("User ID")]
public int UserId { get; set; }

这是我可以通过MultipleRequiredAttribute 自定义数据注释类得到的:

public class MultipleRequiredAttribute : ValidationAttribute, IClientValidatable
{
    private readonly string[] _fields;
    public MultipleRequiredAttribute(params string[] fields)
    {
        _fields = fields;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // If any field has value, then all must have value
        var anyHasValue = _fields.Any(f => !string.IsNullOrEmpty(f));

        if (!anyHasValue) return null;

        foreach (var field in _fields)
        {
            var property = validationContext.ObjectType.GetProperty(field);
            if (property == null)
                return new ValidationResult($"Property '{field}' is undefined.");

            var fieldValue = property.GetValue(validationContext.ObjectInstance, null);

            if (string.IsNullOrEmpty(fieldValue?.ToString()))
                return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
        }

        return null;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new ModelClientValidationRule
        {
            ErrorMessage = ErrorMessage,
            ValidationType = "multiplerequired"
        };
    }
}

谢谢。

【问题讨论】:

  • 你为 jquery 构建一个自定义函数 Validate js plugin on client
  • 从阅读The Complete Guide To Validation In ASP.NET MVC 3 - Part 2开始。在您的 GetClientValidationRules() 方法中,您添加一个 ModelClientValidationRule ,您可以在其中传递一个(比如说)逗号分隔的属性名称列表 - 即您的 fields 值 - 可以在客户端脚本中解析和使用(如果您的有问题,让我知道,我会添加一个答案,但几个小时内没有机会)
  • 谢谢,@StephenMuecke!我的问题之一是如何将值传递给客户端。
  • 你的问题状态 如果一个有一个值,它们都必须有一个值,但你的代码没有验证这一点(你还需要将该属性应用于所有属性如果是这样)
  • 另外,您的return new ValidationResult($"Property '{field}' is undefined."); 也没有任何意义(在视图中显示该消息对用户来说毫无意义且令人困惑) - 要么忽略它,要么在构造函数中签入并抛出异常

标签: c# asp.net asp.net-mvc validation data-annotations


【解决方案1】:

为了获得客户端验证,您需要使用规则ValidationParameters属性的.Add()方法传递ModelClientValidationRule中的'其他属性'的值,然后编写客户端脚本将规则添加到$.validator

但首先,您的属性还有一些其他问题需要解决。首先,只有在应用属性的属性值为null 时,才应执行foreach 循环。其次,如果“其他属性”之一不存在,则返回 ValidationResult 会使用户感到困惑和毫无意义,您应该忽略它。

属性代码应该是(注意我改了属性名)

public class RequiredIfAnyAttribute : ValidationAttribute, IClientValidatable
{
    private readonly string[] _otherProperties;
    private const string _DefaultErrorMessage = "The {0} field is required";

    public RequiredIfAnyAttribute(params string[] otherProperties)
    {
        if (otherProperties.Length == 0) // would not make sense
        {
            throw new ArgumentException("At least one other property name must be provided");
        }
        _otherProperties = otherProperties;
        ErrorMessage = _DefaultErrorMessage;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null) // no point checking if it has a value
        {
            foreach (string property in _otherProperties)
            {
                var propertyName = validationContext.ObjectType.GetProperty(property);
                if (propertyName == null)
                {
                    continue;
                }
                var propertyValue = propertyName.GetValue(validationContext.ObjectInstance, null);
                if (propertyValue != null)
                {
                    return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
                }
            }
        }
        return ValidationResult.Success;
    }
    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ValidationType = "requiredifany",
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
        };
        / pass a comma separated list of the other propeties
        rule.ValidationParameters.Add("otherproperties", string.Join(",", _otherProperties));
        yield return rule;
    }
}

脚本将是

sandtrapValidation = {
    getDependentElement: function (validationElement, dependentProperty) {
        var dependentElement = $('#' + dependentProperty);
        if (dependentElement.length === 1) {
            return dependentElement;
        }
        var name = validationElement.name;
        var index = name.lastIndexOf(".") + 1;
        var id = (name.substr(0, index) + dependentProperty).replace(/[\.\[\]]/g, "_");
        dependentElement = $('#' + id);
        if (dependentElement.length === 1) {
            return dependentElement;
        }
        // Try using the name attribute
        name = (name.substr(0, index) + dependentProperty);
        dependentElement = $('[name="' + name + '"]');
        if (dependentElement.length > 0) {
            return dependentElement.first();
        }
        return null;
    }
}

$.validator.unobtrusive.adapters.add("requiredifany", ["otherproperties"], function (options) {
    var element = options.element;
    var otherNames = options.params.otherproperties.split(',');
    var otherProperties = [];
    $.each(otherNames, function (index, item) {
        otherProperties.push(sandtrapValidation.getDependentElement(element, item))
    });
    options.rules['requiredifany'] = {
        otherproperties: otherProperties
    };
    options.messages['requiredifany'] = options.message;
});

$.validator.addMethod("requiredifany", function (value, element, params) {
    if ($(element).val() != '') {
        // The element has a value so its OK
        return true;
    }
    var isValid = true;
    $.each(params.otherproperties, function (index, item) {
        if ($(this).val() != '') {
            isValid = false;
        }
    });
    return isValid;
});

【讨论】:

  • 它是一个通用函数,用于在 DOM 中查找关联元素。您可能有一个包含复杂对象或集合属性的模型,因此它可能会呈现为(例如)name="Employees[0].FirstName" id="Employees_0__FirstName"name="Employees[0].LastName" id="Employees_0__LastName" 的输入。因此,假设您要验证 LastName 是否提供了 FirstName。您可以在 GetClientValidationRules() 方法中传递的只是另一个属性的名称,即 LastName
  • 该方法首先检查 DOM 是否包含带有id="LastName" 的元素。对于一个简单的对象,它将返回一个元素。但在这种情况下它不会,因此函数的下一部分获取当前元素的名称(即name="Employees[0].FirstName")并获取最后一个点左侧的部分(Employees[0])并附加到其他属性生成Employees[0].LastName。因为通过id 搜索比通过name 属性搜索更快,所以.replace() 生成Employees_0__LastName 并执行对该元素的搜索。
  • 在大多数情况下会找到你想要的,但有些用户会覆盖id 属性所以什么都找不到,最后检查是否基于基于name 属性查找元素.
  • 作为旁注,我稍微简化了该方法,并省略了一些与复选框和单选按钮相关的代码,这些元素需要以不同的方式处理,但我认为这不会适用于您的情况
  • “别名”只是对函数进行命名空间(它是我的 jquery 插件的一部分,其中包括许多不是由内置验证属性处理的验证函数)并且只是防止任何可能的(尽管不太可能)与插件冲突
猜你喜欢
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-30
相关资源
最近更新 更多