ASP.NET MVC 自定义多字段验证
ASP.NET MVC custom multiple fields validation
我正在开发一个 ASP.NET MVC 5.2.3 自定义数据注释,用于在 Visual Studio 2015 年进行验证。它需要采用任意数量的字段并确保如果有一个值,它们都必须有一个值;如果都是null/blank,应该没问题。
几个例子很有帮助:
- ASP.NET MVC implement custom validator use IClientValidatable
- MVC Form Validation on Multiple Fields
- http://www.macaalay.com/2014/02/24/unobtrusive-client-and-server-side-age-validation-in-mvc-using-custom-data-annotations/
但是,我不确定如何在要验证的字段数量未知的情况下进行客户端验证。
如何使用 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
自定义数据注释所能获得的最大信息 class:
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"
};
}
}
谢谢。
为了获得客户端验证,您需要使用规则 ValidationParameters
的 .Add()
方法在 ModelClientValidationRule
中传递 'other properties' 的值属性,然后编写客户端脚本将规则添加到 $.validator
。
但首先,您的属性还有一些其他问题需要解决。首先,只有当您应用属性的 属性 的值为 null
时,您才应该执行 foreach
循环。其次,如果 'other properties' 之一不存在,则返回 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;
});
我正在开发一个 ASP.NET MVC 5.2.3 自定义数据注释,用于在 Visual Studio 2015 年进行验证。它需要采用任意数量的字段并确保如果有一个值,它们都必须有一个值;如果都是null/blank,应该没问题。
几个例子很有帮助:
- ASP.NET MVC implement custom validator use IClientValidatable
- MVC Form Validation on Multiple Fields
- http://www.macaalay.com/2014/02/24/unobtrusive-client-and-server-side-age-validation-in-mvc-using-custom-data-annotations/
但是,我不确定如何在要验证的字段数量未知的情况下进行客户端验证。
如何使用 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
自定义数据注释所能获得的最大信息 class:
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"
};
}
}
谢谢。
为了获得客户端验证,您需要使用规则 ValidationParameters
的 .Add()
方法在 ModelClientValidationRule
中传递 'other properties' 的值属性,然后编写客户端脚本将规则添加到 $.validator
。
但首先,您的属性还有一些其他问题需要解决。首先,只有当您应用属性的 属性 的值为 null
时,您才应该执行 foreach
循环。其次,如果 'other properties' 之一不存在,则返回 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;
});