其实是很少发技术类文章的, 只是在最近的一次项目中,由于这个位置的技术短板导致折腾好几天,而网上搜的内容又都不直接说明,所以还是决定写出来给人参考一下, 可以帮助大家省点时间。
先来看一下需求:
很是草稿的图片请别介意。需求是这样,当选中图中的下拉菜单选项为第一个时,使用文字验证,当下拉菜单选为第二个选项时,使用数字验证。
这种需求其实比较普遍,很多流程化管理的页面都会有这种需求。比如在申请证件时候可选项是护照还是身份证的时候,对填入内容的验证规则可以是不一样的。
但微软自带的model绑定规则是只能适用一种规则,因此我们需要自己重写验证规则来满足我们的需求。
因此,我们写下的代码可能会是这样的:
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
这只是实现了其中一种“必填”的策略罢了
所以我们要解决我们面临的问题的第一步是需要做一个定制化的属性标签。
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class CustomdropdownlistAttribute : ValidationAttribute, IClientValidatable
{
private const string _defaultErrorMessage = "‘{0}‘ must be at least {1} characters long.";
private readonly int _minCharacters = Membership.Provider.MinRequiredPasswordLength;
public CustomdropdownlistAttribute()
: base(_defaultErrorMessage)
{
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentCulture, ErrorMessageString,
name, _minCharacters);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
string valueAsString = value as string;
var containerType = validationContext.ObjectInstance.GetType();
var field = containerType.GetProperty("MydSelect");
return (int)field.GetValue(validationContext.ObjectInstance, null) == 0 ? ValidationResult.Success : new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
}
//机理是通过validationContext获取当前model的所有属性,通过反射获取要验证的字段的值,“MydSelect”是下拉菜单的字段名称
public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
return new[]{
new ModelClientValidationRule{
ValidationType="validatedropdownlist",
ErrorMessage="must be int"//FormatErrorMessage(metadata.DisplayName)//this.ErrorMessage
}
};
}
}
Customdropdownlist标签实现后就可以用来装饰我们需要验证的model对象里的字段。
[Customdropdownlist]
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
这个个性化实现的标签可以直接扔在viewmodel类里,因为每个model的内的验证规则或许会有细微差别, 随着业务发展,也会出现更多页面出现类似的需求。届时我们可以把它们抽出成一个统一的类。
此时,其实完成了服务端的验证过程,你需要做的就是根据你的需要来实现 ValidationResult IsValid(object value, ValidationContext validationContext)这个函数。
不过要记住要把这个函数的实现用overide 和protected,不然后面报一个很奇怪的错,表示有一个bool IsValid(object value)的函数没有被重写的错误。 原因是虽然都是IsValid,但是之间存在相互调用的关系。 因此如果被声明成public,就会报错。
以上都是model.cs文件中关于model的服务端验证代码。
要想真正实现客户端验证,还需要在你的.cshtml文件中注册相应的JS脚本。
jQuery.validator.unobtrusive.adapters.add("validatedropdownlist", function (options) {
options.rules["validatedropdownlist"] = true;
if (options.message) {
options.messages["validatedropdownlist"] = options.message;
}
});
jQuery.validator.addMethod("validatedropdownlist", function (value) {
if (($(‘#dropdownlist‘).val() == 0)) return true
});
网上有人说validator适配器上方法名validatedropdownlist一定要全小写, 我没试过,但至少全小写是可以work.于是不想浪费时间地全部小写了。
其中$((‘#dropdownlist‘))是dropdownlist在运行后页面中下拉框的名字, 我们其实可以把这个名字作为参数传到页面中,这里只是偷了个懒,请不要在意这些细节 。
最后,不要忘了在web.config中打开客户端验证
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
这里还有篇外国人写的很不错的过程, 可以用来参考和扩展功能。
http://anthonyvscode.com/2011/07/14/mvc-3-requiredif-validator-for-multiple-values/