zoukankan      html  css  js  c++  java
  • Pro ASP.NET MVC 3 Framework 读书笔记之数据校验

    MVC3.0中提供了丰富的Model数据校验,这对于数据开发是非常方便,校验按实现方式来分,有如下几类:

    1. 在controller中进行校验
    2. 在Model上的属性的元数据上面加入验证逻辑
    3. ModelBinder上进行校验,并可以自定义属性进行验证
    4. Model自校验
    5. 自定义ValidateProvider进行校验
    6. 手动进行数据校验
    7. 远程校验(Ajax进行校验)

    从客户端和服务端来分:

    1,2,3,4,5,7属于服务端校验;

    2,6属于客户端校验

    1、在Controller进行校验

    这是一种最直接的实现方式

    示例代码如下:

       1: [HttpPost]
       2: public ViewResult MakeBooking(Appointment appt) {
       3:     if (string.IsNullOrEmpty(appt.ClientName)) {
       4:         ModelState.AddModelError("ClientName", "Please enter your name");
       5:     }
       6:     if (ModelState.IsValidField("Date") && DateTime.Now > appt.Date) {
       7:         ModelState.AddModelError("Date", "Please enter a date in the future");
       8:     }
       9:     if (!appt.TermsAccepted) {
      10:         ModelState.AddModelError("TermsAccepted", "You must accept the terms");
      11:     }
      12:     if (ModelState.IsValid) {
      13:         repository.SaveAppointment(appt);
      14:         return View("Completed", appt);
      15:     } else {
      16:         return View();
      17:     }
      18: }

    通过上面的代码把出错信息设置Controller中ModelState中,这样就可以通过判断ModelState.IsValid进行验证Model是否通过验证,上面的代码演示,如果通过验证,就返回保存成功的视图,如果失败就刷新当前页面,并显示错误信息.

    2、在Model上的属性的元数据上面加入验证逻辑

    在属性的元数据上面加入验证逻辑,这是一种使用比较多方式,在最早的Winform也很多的使用这种方式进行校验,这种方式不是MVC特有的方式,同时还提供了对客户端校验的支持(这种支持不知道是不是在MVC中加入的),很好的实现客户端和服务端同时进行校验的问题。

    这种实现的示例如下所示:

       1: public class Appointment {
       2:        [Required]
       3:        public string ClientName { get; set; }
       4:        [DataType(DataType.Date)]
       5:        [Required(ErrorMessage="Please enter a date")]
       6:        public DateTime Date { get; set; }
       7:        [Range(typeof(bool), "true", "true", ErrorMessage="You must accept the terms")]
       8:        public bool TermsAccepted { get; set; }
       9:    }

    系统定义的属性有:

    属性 示例 备注
    Compare [Conmpre(“其他属性”)]  
    Range [Range(10,20)]  
    RegularExpression [RegularExpression(“pattern”)]  
    Required [Required]  
    StringLength [StringLength(10)]  

    同时,我们还可以自定义校验属性,并加到Model属性或其自身上,示例属性如下:

       1: public class MustBeTrueAttribute : ValidationAttribute {
       2:     public override bool IsValid(object value) {
       3:         return value is bool && (bool)value;
       4:     }
       5: }
       6:  
       7: public class FutureDateAttribute : RequiredAttribute {
       8:     public override bool IsValid(object value) {
       9:         return base.IsValid(value) && 
      10:             value is DateTime &&  
      11:             ((DateTime)value) > DateTime.Now;
      12:     }
      13: }
      14: public class AppointmentValidatorAttribute : ValidationAttribute {
      15:     public AppointmentValidatorAttribute() {
      16:         ErrorMessage = "Joe cannot book appointments on Mondays";
      17:     }
      18: public override bool IsValid(object value) {
      19:        Appointment app = value as Appointment;
      20:        if (app == null || string.IsNullOrEmpty(app.ClientName) || app.Date == null) {
      21:            // we don't have a model of the right type to validate, or we don't have
      22:            // the values for the ClientName and Date properties we require
      23:            return true;
      24:        } else {
      25:            return !(app.ClientName == "Joe" && app.Date.DayOfWeek == DayOfWeek.Monday);
      26:        } 
      27:    }

    如果自定义属性实现了接口:

    image

    就会自动实现客户端校验。

    3、ModelBinder上进行校验,并可以自定义属性进行验证

    DefaultModelBinder实现了基本数据校验功能,在客户端提交数据到服务端后,默认ModelBinder会自动从表单中解析出相应属性并给相应的Model属性赋值,同时会自动调用属性的校验设置,如果要统一实现数据校验,可以通过继承“DefaultModelBinder”来实现自定义的通用校验,我们只需要重写此类的两个方法,如下表所示:

    方法 描述 默认实现
    OnModelUpdate 当ModelBinder要试着把所有属性赋值给实体时,会自动调用。 通过自动调用元数据设置进行校验,并把校验错误写入到ModelState中。
    SetProperty 当ModelBinder要给指定属性进行赋值时,就就会自动调用此方法 。 如果要设置的变量值为null或没有变量进行赋值,就会在ModelState中注册“The <name> field is required”错误信息;如果变量值不能被解析,就会在ModelState中注册出错信息“The value <value> is not valid for <name>”

    示例如下:

       1: public class ValidatingModelBinder : DefaultModelBinder {
       2:        protected override void SetProperty(ControllerContext controllerContext, 
       3:            ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, 
       4:            object value) {
       5:            // make sure we call the base implementation
       6:            base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
       7:            // perform our property-level validation
       8:            switch (propertyDescriptor.Name) {
       9:                case "ClientName":
      10:                    if (string.IsNullOrEmpty((string)value)) {
      11:                        bindingContext.ModelState.AddModelError("ClientName", 
      12:                            "Please enter your name");
      13:                    }
      14:                    break;
      15:                case "Date":
      16:                    if (bindingContext.ModelState.IsValidField("Date") && 
      17:                        DateTime.Now > ((DateTime)value)) {
      18:                        bindingContext.ModelState.AddModelError("Date", 
      19:                            "Please enter a date in the future");
      20:                    }
      21:                    break;
      22:                case "TermsAccepted":
      23:                    if (!((bool)value)) {
      24:                        bindingContext.ModelState.AddModelError("TermsAccepted", 
      25:                            "You must accept the terms");
      26:                    }
      27:                    break;
      28:            }
      29:        }
      30:        protected override void OnModelUpdated(ControllerContext controllerContext,
      31:            ModelBindingContext bindingContext) {
      32:            // make sure we call the base implementation
      33:            base.OnModelUpdated(controllerContext, bindingContext);
      34:            // get the model
      35:            Appointment model = bindingContext.Model as Appointment;
      36: // apply our model-level validation
      37:             if (model != null &&
      38:                 bindingContext.ModelState.IsValidField("ClientName") &&
      39:                 bindingContext.ModelState.IsValidField("Date") &&
      40:                 model.ClientName == "Joe" &&
      41:                 model.Date.DayOfWeek == DayOfWeek.Monday) {
      42:                 bindingContext.ModelState.AddModelError("",
      43:                     "Joe cannot book appointments on Mondays");
      44:             }
      45:         }
      46:     }
      47: }

    自定义的ModelBinder安装方法如下:

       1: protected void Application_Start() {
       2:     AreaRegistration.RegisterAllAreas();
       3:     ModelBinders.Binders.Add(typeof(Appointment), new ValidatingModelBinder());
       4:     RegisterGlobalFilters(GlobalFilters.Filters);
       5:     RegisterRoutes(RouteTable.Routes);
       6: }

    注册方法,是对指定的类型进行设置指定的ModelBinder.

    4、在Model上面实现校验接口

    实现自校验的Model,就要Model实现接口:image这个接口只有一个方法,

    image

    当ModelBinder赋值给Model每个属性时,会自动调用这个接口进行数据校验

    示例代码如下:

       1: public class Appointment : IValidatableObject {
       2:     public string ClientName { get; set; }
       3:     [DataType(DataType.Date)]
       4:     public DateTime Date { get; set; }
       5:     public bool TermsAccepted { get; set; }
       6:     public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
       7:         List<ValidationResult> errors = new List<ValidationResult>();
       8:         if (string.IsNullOrEmpty(ClientName)) {
       9:             errors.Add(new ValidationResult("Please enter your name"));
      10:        }
      11:         if (DateTime.Now > Date) {
      12:             errors.Add(new ValidationResult("Please enter a date in the future"));
      13:         }
      14:         if (errors.Count == 0 && ClientName == "Joe" 
      15:             && Date.DayOfWeek == DayOfWeek.Monday) {
      16:             errors.Add(new ValidationResult("Joe cannot book appointments on Mondays"));
      17:         }
      18:         if (!TermsAccepted) {
      19:             errors.Add(new ValidationResult("You must accept the terms"));
      20:         }
      21:         return errors;
      22:     }
      23: }

    5、自定义ValidateProvider进行校验

    自定义ValidateProvider,通常都是从“ ModelValidationProvider ”继承并重载方法“GetValidators”来实现,接口签名如下:

       1: IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)

    这个方法由Model的每个属性调用一次,再Model自身调用一次,如果不对Model进行校验,可以返回“Enumerable.Empty<ModelValidator>()”

    接口参数“ModelMetadata metadata”关键属性说明如下:

    属性 描述
    ContainerType 属性类型
    PropertyName 属性名称
    ModelType Model类型

    示例代码如下:

       1: public class CustomValidationProvider : ModelValidatorProvider {
       2:     public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata,
       3:         ControllerContext context) {
       4: if (metadata.ContainerType == typeof(Appointment)) {
       5:            return new ModelValidator[] {
       6:            new AppointmentPropertyValidator(metadata, context)
       7:        };
       8:        } else if (metadata.ModelType == typeof(Appointment)) {
       9:            return new ModelValidator[] { 
      10:            new AppointmentValidator(metadata, context) 
      11:        };
      12:        }
      13:        return Enumerable.Empty<ModelValidator>();
      14:    }
      15:  
      16: public class AppointmentPropertyValidator : ModelValidator {
      17:     public AppointmentPropertyValidator(ModelMetadata metadata, ControllerContext context)
      18:         : base(metadata, context) {
      19:     }
      20:     public override IEnumerable<ModelValidationResult> Validate(object container) {
      21:         Appointment appt = container as Appointment;
      22:         if (appt != null) {
      23:             switch (Metadata.PropertyName) {
      24:                 case "ClientName":
      25:                     if (string.IsNullOrEmpty(appt.ClientName)) {
      26:                         return new ModelValidationResult[] {
      27:                         new ModelValidationResult {
      28:                             MemberName = "ClientName",
      29:                             Message = "Please enter your name"
      30:                         }};
      31:                     }
      32:                     break;
      33:                 case "Date":
      34:                     if (appt.Date == null || DateTime.Now > appt.Date) {
      35:                         return new ModelValidationResult[] {
      36:                         new ModelValidationResult {
      37:                         MemberName = "Date",
      38:                         Message = "Please enter a date in the future"
      39:                     }};
      40:                     }
      41: break;
      42:                case "TermsAccepted":
      43:                    if (!appt.TermsAccepted) {
      44:                        return new ModelValidationResult[] {
      45:                        new ModelValidationResult {
      46:                            MemberName = "TermsAccepted",
      47:                            Message = "You must accept the terms"
      48:                        }};
      49:                    }
      50:                    break;
      51:            }
      52:        }
      53:        return Enumerable.Empty<ModelValidationResult>();
      54:    }
      55: public class AppointmentValidator : ModelValidatorCustomValidator<Appointment> {
      56:     public AppointmentValidator(ModelMetadata metadata, ControllerContext context)
      57:         : base(metadata, context) {
      58:     }
      59:     public override void Validate(Appointment container, 
      60:         IList<ModelValidationResult> errors) {
      61:         Appointment appt = (Appointment)Metadata.Model;
      62:         if (appt.ClientName == "Joe" && appt.Date.DayOfWeek == DayOfWeek.Monday) {
      63:             errors.Add(new ModelValidationResult {
      64:                 MemberName = "",
      65:                 Message = "Joe cannot book appointments on Mondays"
      66:             });
      67:         }
      68:     }
      69: }

    注册方法:

       1: protected void Application_Start() {
       2:     AreaRegistration.RegisterAllAreas();
       3:     ModelValidatorProviders.Providers.Add(new CustomValidationProvider());
       4:     RegisterGlobalFilters(GlobalFilters.Filters);
       5:     RegisterRoutes(RouteTable.Routes);
       6: }

    7、远程校验

    远程校验通过Ajax调用Controller的Action来实现

    样例Action如下:

       1: public JsonResult ValidateDate(string Date) {
       2:         DateTime parsedDate;
       3:         if (!DateTime.TryParse(Date, out parsedDate)) {
       4:             return Json("Please enter a valid date (mm/dd/yyyy)",
       5:                 JsonRequestBehavior.AllowGet);
       6:         } else if (DateTime.Now > parsedDate) {
       7:             return Json("Please enter a date in the future", JsonRequestBehavior.AllowGet);
       8:         } else {
       9:             return Json(true, JsonRequestBehavior.AllowGet);
      10:         }
      11:     }

    注:所有代码都来源于(仅仅作样例):

    image

  • 相关阅读:
    【Quartz】1、Quartz使用说明
    【Servlet】1、Servlet监听器及相关接口
    【IDEA&&Eclipse】5、IntelliJ IDEA常见配置
    Chris Richardson微服务实战系列
    Traefik Kubernetes 初试
    用友iuap云运维平台支持基于K8s的微服务架构
    DCOS中监控和弹性伸缩方案经验
    使用微服务架构改造遗留系统
    kubernetes中port、target port、node port的对比分析,以及kube-proxy代理
    基于prometheus监控k8s集群
  • 原文地址:https://www.cnblogs.com/LifelongLearning/p/2106787.html
Copyright © 2011-2022 走看看