-
验证 Validation
多样化验证规则
最常见的验证方式是:在实体的属性上加 特性(Attribute) 的方式来完成基本的数据验证. 比如 Required, StringLength, Range 等. 为了保持实体类的POCO
( Plain Old CLR Objects, 所谓的POCO就是那些不包括INSERT、ADD、DEL等数据持久化操作的以及不包括任何业务逻辑功能的原始类。只包含最基本的GETTER 和SETTER).,一般是对实体类声明一个伴随类(MetadataTypeAttribute),在伴随类里声明各种特性.但是伴随类只能声明一个(可以尝试对实体类加多个
MetadataType 看看).
实际情况下, 不同的系统可能要求不一样的验证规则,但是又用的是同一套实体.
比如旅客信息的邮件地址,电话号码等, 在线下系统下是非必填的,但是在线上预订的时候,又是必填的. 你可以定意两个不同的实体. 我采用的是另外一种方法:
先声明一个伴随类:
public class EContactMetadata {
[RegularExpression(@"\d{8}" , ErrorMessage = "請輸入8位有效的號碼")]
[Required]
public object PhoneNo1 { get; set; }
}
在 Global 里把这个伴随类注册到实体类上:
protected void Application_Start() {
…
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(EContact) , typeof(EContactMetadata)) , typeof(EContact));
…
}
用这种方式可以对同一个实体类添加多个伴随类,后注册的会覆盖先注册的.没有发生覆盖的会保留.
部分验证
某个实体类的属性对应到数据库里的某个字段是必填的,但是在填写界面里,又用不到这个字段,而且这个字段暂时也没有办法生成.
比如旅客信息的 OrderNo 是必填的,但是在填写信息页面, OrderNo 还没有生成. 所以在 Action 里, ModelState.IsValid 一直是 false. 为了避免这个 false, 你可以新定义一个类, 但是又不能每一种变化都搞个类出来吧.
使用 BindAttribute 接合 ModelBinder
BindAttribute 有 Include 和 Exclude , Include 是只接收 Include 指定的属性, Exclude 是排除.
[HttpPost]
public ActionResult Reserve([Bind(Include = "FG,FB,Hotels,OptionUseDates")]Reserve r , string act) {
…
意思是参数 r 只接收 Post 过来的 FG, FB,Hotels,OptionUseDates 数据,其它的传过来也不要
在ModelBinder 中把非 Include 的或 Exclude 的验证错误剔除
public class SmartModelBinder : DefaultModelBinder {
protected override void OnModelUpdated(ControllerContext controllerContext , ModelBindingContext bindingContext) {
Dictionary<string , bool> startedValid = new Dictionary<string , bool>(StringComparer.OrdinalIgnoreCase);
//获取模型的验证结果
var results = ModelValidator.GetModelValidator(bindingContext.ModelMetadata , controllerContext).Validate(bindingContext.Model);
foreach(ModelValidationResult validationResult in results) {
string subPropertyName = CreateSubPropertyName(bindingContext.ModelName , validationResult.MemberName);
//if(bindingContext.PropertyFilter(subPropertyName)) {
//bindingContext.PropertyFilter 是一个 delegate, 如果指定的 member 在 BindAttribute 的 Include 的列表内(或者非 Exclude 的列表内),返回 true, 否则为 false
//部分验证的功能就是通过它的结果来实现的
if(bindingContext.PropertyFilter(validationResult.MemberName)) {
if(!startedValid.ContainsKey(subPropertyName)) {
startedValid[subPropertyName] = bindingContext.ModelState.IsValidField(subPropertyName);
}
if(startedValid[subPropertyName]) {
bindingContext.ModelState.AddModelError(subPropertyName , validationResult.Message);
}
}
}
}
在 Global 里注册该 ModelBinder
protected void Application_Start() {
…
ModelBinders.Binders.DefaultBinder = new SmartModelBinder();
…
自验证 IValidatableObject
某些情况下,简单的在用 DataAnnoation 不足以做完业务逻辑的验证。比如旅客信息中,儿童的年龄要在返程日期的为准,2 到 11 岁之间, 当机票订票要求是护照时,出生日期、护照号码,签发国家、签发地为必填;当要求是回乡证时,回乡证号必填;当要求护照或回乡证时,其中之一的信息必须完整。
这种情况下,就需要用到 IValidatableObject , 只要按需求实现 Validate 方法就可以了。
public class Traveller : IValidatableObject {
public int TravellerID { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public DateTime Birthday { get; set; }
public bool IsAdult { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
if(!this.IsAdult && this.Birthday.Date < DateTime.Now.AddYears(-12)) {
yield return new ValidationResult("儿童年龄必须在 12 岁以内");
}
}
}
另外注意,如果标注有 DataAnnation 的属性没有验证没有通过,是不会去执行 Validate 方法的。
手动验证
暂未整理
启用客服端验证
AppSetting 里
<!--啟用客戶端校驗-->
<add key="ClientValidationEnabled" value="true"/>
<!—启用非介入式验证-->
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
或
Html.EnableClientValidation(true);
Html.EnableUnobtrusiveJavaScript(true)
另外,如果以启用了客户端验证,但是表单项并没有在 Form (Html.BeginForm()) 内,也是不会有客户端验证的.
自定义客户端验证
具体请参考该 js 文件。
以下所述的客户端验证都是基于 jQuery.validate 1.9 + jquery.validate.unobtrusive (项目自带) 的
默认的,要在客户端实时看到验证信息(js 验证),需要使用 Html.ValidationMessageFor() 来生成一个容器,供 js 往里填写错误信息.但是,一个页面如果有很多个表单对象,而又不能使用 EditorForModel , 一个一个敲,也是一件很头痛的事。其实可以通过修改,来自动生成: 重写 jQuery validator 的 setting 中的 errorPlacement 和 success 。写这两个是做到最小的修改,如果有必要,你也可以重写 其它的方法。
mode1_error 即 errorPlacement
var mode1_error = function(error, inputElement){
//查找错误提示容器
var container = $("span[data-valmsg-for='" + inputElement[0].name + "']", inputElement[0].form);