对于任何一个应用而言在客户端做的数据有效性验证都不是安全有效的,这时候就要求我们在开发的时候在服务端也对数据的有效性进行验证。SpringMVC自身对数据在服务端的校验有一个比较好的支持,它能将我们提交到服务端的数据按照我们事先的约定进行数据有效性验证,对于不合格的数据信息SpringMVC会把它保存在错误对象中,这些错误信息我们也可以通过SpringMVC提供的标签在前端JSP页面上进行展示。

使用Validator接口进行验证

在SpringMVC中提供了一个Validator接口,我们可以通过该接口来定义我们自己对实体对象的验证。接下来看一个示例。

假设我们现在有一个需要进行验证的实体类User,其代码如下所示:

Java代码  
  1. public class User {
  2. private String username;
  3. private String password;
  4. public String getUsername() {
  5. return username;
  6. }
  7. public void setUsername(String username) {
  8. this.username = username;
  9. }
  10. public String getPassword() {
  11. return password;
  12. }
  13. public void setPassword(String password) {
  14. this.password = password;
  15. }
  16. public String toString() {
  17. return username + ", " + password;
  18. }
  19. }

那么当我们需要使用SpringMVC提供的Validator接口来对该实体类进行校验的时候该如何做呢?这个时候我们应该提供一个Validator的实现类,并实现Validator接口的supports方法和validate方法。Supports方法用于判断当前的Validator实现类是否支持校验当前需要校验的实体类,只有当supports方法的返回结果为true的时候,该Validator接口实现类的validate方法才会被调用来对当前需要校验的实体类进行校验。这里假设我们需要验证User类的username和password都不能为空,先给出其代码,稍后再进行解释。这里我们定义一个UserValidator,其代码如下:

Java代码  
  1. import org.springframework.validation.Errors;
  2. import org.springframework.validation.ValidationUtils;
  3. import org.springframework.validation.Validator;
  4. public class UserValidator implements Validator {
  5. public boolean supports(Class<?> clazz) {
  6. // TODO Auto-generated method stub
  7. return User.class.equals(clazz);
  8. }
  9. public void validate(Object obj, Errors errors) {
  10. // TODO Auto-generated method stub
  11. ValidationUtils.rejectIfEmpty(errors, "username", null, "Username is empty.");
  12. User user = (User) obj;
  13. if (null == user.getPassword() || "".equals(user.getPassword()))
  14. errors.rejectValue("password", null, "Password is empty.");
  15. }
  16. }

在上述代码中我们在supports方法中定义了该UserValidator只支持对User对象进行校验。在validate方法中我们校验了User对象的username和password不为empty的情况,这里的empty包括null和空字符串两种情况。ValidationUtils类是spring中提供的一个工具类。Errors就是Spring用来存放错误信息的对象。

我们已经定义了一个对User类进行校验的UserValidator了,但是这个时候UserValidator还不能对User对象进行校验,因为我们还没有告诉Spring应该使用UserValidator来校验User对象。在SpringMVC中我们可以使用DataBinder来设定当前Controller需要使用的Validator。先来看下面一段代码:

Java代码  
  1. import javax.validation.Valid;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.validation.BindingResult;
  4. import org.springframework.validation.DataBinder;
  5. import org.springframework.web.bind.annotation.InitBinder;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. @Controller
  8. public class UserController {
  9. @InitBinder
  10. public void initBinder(DataBinder binder) {
  11. binder.setValidator(new UserValidator());
  12. }
  13. @RequestMapping("login")
  14. public String login(@Valid User user, BindingResult result) {
  15. if (result.hasErrors())
  16. return "redirect:user/login";
  17. return "redirect:/";
  18. }
  19. }

在上面这段代码中我们可以看到我们定义了一个UserController,该Controller有一个处理login操作的处理器方法login,它需要接收客户端发送的一个User对象,我们就是要利用前面的UserValidator对该User对象进行校验。首先我们可以看到我们login方法接收的参数user是用@Valid进行标注的,这里的@Valid是定义在JSR-303标准中的,我这里使用的是hibernate Validation对它的实现。这里我们必须使用@Valid标注我们需要校验的参数user,否则Spring不会对它进行校验。另外我们的处理器方法必须给定包含Errors的参数,这可以是Errors本身,也可以是它的子类BindingResult,使用了Errors参数就是告诉Spring关于表单对象数据校验的错误将由我们自己来处理,否则Spring会直接抛出异常,而且这个参数是必须紧挨着@Valid参数的,即必须紧挨着需要校验的参数,这就意味着我们有多少个@Valid参数就需要有多少个对应的Errors参数,它们是一一对应的。前面有提到我们可以通过DataBinder来指定需要使用的Validator,我们可以看到在上面代码中我们通过@InitBinder标记的方法initBinder设置了当前Controller需要使用的Validator是UserValidator。这样当我们请求处理器方法login时就会使用DataBinder设定的UserValidator来校验当前的表单对象User,首先会通过UserValidator的supports方法判断其是否支持User对象的校验,若支持则调用UserValidator的validate方法,并把相关的校验信息存放到当前的Errors对象中。接着我们就可以在我们的处理器方法中根据是否有校验异常信息来做不同的操作。在上面代码中我们定义了在有异常信息的时候就跳转到登陆页面。这样我们就可以在登陆页面上通过errors标签来展示这些错误信息了。

我们知道在Controller类中通过@InitBinder标记的方法只有在请求当前Controller的时候才会被执行,所以其中定义的Validator也只能在当前Controller中使用,如果我们希望一个Validator对所有的Controller都起作用的话,我们可以通过WebBindingInitializer的initBinder方法来设定了。另外,在SpringMVC的配置文件中通过mvc:annotation-driven的validator属性也可以指定全局的Validator。代码如下所示:

Xml代码  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:mvc="http://www.springframework.org/schema/mvc"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context-3.0.xsd
  9. http://www.springframework.org/schema/mvc
  10. http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
  11. <mvc:annotation-driven validator="userValidator"/>
  12. <bean id="userValidator" class="com.xxx.xxx.UserValidator"/>
  13. ...
  14. </beans>

使用JSR-303 Validation进行验证

JSR-303是一个数据验证的规范,这里我不会讲这个规范是怎么回事,只会讲一下JSR-303在SpringMVC中的应用。JSR-303只是一个规范,而Spring也没有对这一规范进行实现,那么当我们在SpringMVC中需要使用到JSR-303的时候就需要我们提供一个对JSR-303规范的实现,Hibernate Validator是实现了这一规范的,这里我将以它作为JSR-303的实现来讲解SpringMVC对JSR-303的支持。

JSR-303的校验是基于注解的,它内部已经定义好了一系列的限制注解,我们只需要把这些注解标记在需要验证的实体类的属性上或是其对应的get方法上。来看以下一个需要验证的实体类User的代码:

Java代码  
  1. import javax.validation.constraints.Min;
  2. import javax.validation.constraints.NotNull;
  3. import org.hibernate.validator.constraints.NotBlank;
  4. public class User {
  5. private String username;
  6. private String password;
  7. private int age;
  8. @NotBlank(message="用户名不能为空")
  9. public String getUsername() {
  10. return username;
  11. }
  12. public void setUsername(String username) {
  13. this.username = username;
  14. }
  15. @NotNull(message="密码不能为null")
  16. public String getPassword() {
  17. return password;
  18. }
  19. public void setPassword(String password) {
  20. this.password = password;
  21. }
  22. @Min(value=10, message="年龄的最小值为10")
  23. public int getAge() {
  24. return age;
  25. }
  26. public void setAge(int age) {
  27. this.age = age;
  28. }
  29. }

我们可以看到我们在username、password和age对应的get方法上都加上了一个注解,这些注解就是JSR-303里面定义的限制,其中@NotBlank是Hibernate Validator的扩展。不难发现,使用JSR-303来进行校验比使用Spring提供的Validator接口要简单的多。我们知道注解只是起到一个标记性的作用,它是不会直接影响到代码的运行的,它需要被某些类识别到才能起到限制作用。使用SpringMVC的时候我们只需要把JSR-303的实现者对应的jar包放到classpath中,然后在SpringMVC的配置文件中引入MVC Namespace,并加上<mvn:annotation-driven/>就可以非常方便的使用JSR-303来进行实体对象的验证。加上了<mvn:annotation-driven/>之后Spring会自动检测classpath下的JSR-303提供者并自动启用对JSR-303的支持,把对应的校验错误信息放到Spring的Errors对象中。这时候SpringMVC的配置文件如下所示:

Xml代码  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:mvc="http://www.springframework.org/schema/mvc"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context-3.0.xsd
  9. http://www.springframework.org/schema/mvc
  10. http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
  11. <mvc:annotation-driven/>
  12. </beans>

接着我们来定义一个使用User对象作为参数接收者的Controller,其代码如下所示:

Java代码  
  1. import javax.validation.Valid;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.validation.BindingResult;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. @Controller
  6. public class UserController {
  7. @RequestMapping("login")
  8. public String login(@Valid User user, BindingResult result) {
  9. if (result.hasErrors())
  10. return "user/login";
  11. return "redirect:/";
  12. }
  13. }

这样当我们不带任何参数请求login.do的时候就不能通过实体对象User的属性数据有效性限制,然后会把对应的错误信息放置在当前的Errors对象中。

JSR-303原生支持的限制有如下几种

限制

说明

@Null

限制只能为null

@NotNull

限制必须不为null

@AssertFalse

限制必须为false

@AssertTrue

限制必须为true

@DecimalMax(value)

限制必须为一个不大于指定值的数字

@DecimalMin(value)

限制必须为一个不小于指定值的数字

@Digits(integer,fraction)

限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction

@Future

限制必须是一个将来的日期

@Max(value)

限制必须为一个不大于指定值的数字

@Min(value)

限制必须为一个不小于指定值的数字

@Past

限制必须是一个过去的日期

@Pattern(value)

限制必须符合指定的正则表达式

@Size(max,min)

限制字符长度必须在min到max之间

除了JSR-303原生支持的限制类型之外我们还可以定义自己的限制类型。定义自己的限制类型首先我们得定义一个该种限制类型的注解,而且该注解需要使用@Constraint标注。现在假设我们需要定义一个表示金额的限制类型,那么我们可以这样定义:

Java代码  
  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. import javax.validation.Constraint;
  6. import javax.validation.Payload;
  7. import com.xxx.xxx.constraint.impl.MoneyValidator;
  8. @Target({ElementType.FIELD, ElementType.METHOD})
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Constraint(validatedBy=MoneyValidator.class)
  11. public @interface Money {
  12. String message() default"不是金额形式";
  13. Class<?>[] groups() default {};
  14. Class<? extends Payload>[] payload() default {};
  15. }

我们可以看到在上面代码中我们定义了一个Money注解,而且该注解上标注了@Constraint注解,使用@Constraint注解标注表明我们定义了一个用于限制的注解。@Constraint注解的validatedBy属性用于指定我们定义的当前限制类型需要被哪个ConstraintValidator进行校验。在上面代码中我们指定了Money限制类型的校验类是MoneyValidator。另外需要注意的是我们在定义自己的限制类型的注解时有三个属性是必须定义的,如上面代码所示的messagegroupspayload属性。

在定义了限制类型Money之后,接下来就是定义我们的限制类型校验类MoneyValidator了。限制类型校验类必须实现接口javax.validation.ConstraintValidator,并实现它的initialize和isValid方法。我们先来看一下MoneyValidator的代码示例:

Java代码  
  1. import java.util.regex.Pattern;
  2. import javax.validation.ConstraintValidator;
  3. import javax.validation.ConstraintValidatorContext;
  4. import com.xxx.xxx.constraint.Money;
  5. public class MoneyValidator implements ConstraintValidator<Money, Double> {
  6. private String moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式
  7. private Pattern moneyPattern = Pattern.compile(moneyReg);
  8. public void initialize(Money money) {
  9. // TODO Auto-generated method stub
  10. }
  11. public boolean isValid(Double value, ConstraintValidatorContext arg1) {
  12. // TODO Auto-generated method stub
  13. if (value == null)
  14. return true;
  15. return moneyPattern.matcher(value.toString()).matches();
  16. }
  17. }

从上面代码中我们可以看到ConstraintValidator是使用了泛型的。它一共需要指定两种类型,第一个类型是对应的initialize方法的参数类型,第二个类型是对应的isValid方法的第一个参数类型。从上面的两个方法我们可以看出isValid方法是用于进行校验的,有时候我们在校验的过程中是需要取当前的限制类型的属性来进行校验的,比如我们在对@Min限制类型进行校验的时候我们是需要通过其value属性获取到当前校验类型定义的最小值的,我们可以看到isValid方法无法获取到当前的限制类型Money。这个时候initialize方法的作用就出来了。我们知道initialize方法是可以获取到当前的限制类型的,所以当我们在校验某种限制类型时需要获取当前限制类型的某种属性的时候,我们可以给当前的ConstraintValidator定义对应的属性,然后在initialize方法中给该属性赋值,接下来我们就可以在isValid方法中使用其对应的属性了。针对于这种情况我们来看一个代码示例,现在假设我要定义自己的@Min限制类型和对应的MinValidator校验器,那么我可以如下定义:

Min限制类型

Java代码  
  1. @Target({ElementType.FIELD, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Constraint(validatedBy=MinValidator.class)
  4. public @interface Min {
  5. int value() default 0;
  6. String message();
  7. Class<?>[] groups() default {};
  8. Class<? extends Payload>[] payload() default {};
  9. }

MinValidator校验器

Java代码  
  1. public class MinValidator implements ConstraintValidator<Min, Integer> {
  2. private int minValue;
  3. public void initialize(Min min) {
  4. // TODO Auto-generated method stub
  5. //把Min限制类型的属性value赋值给当前ConstraintValidator的成员变量minValue
  6. minValue = min.value();
  7. }
  8. public boolean isValid(Integer value, ConstraintValidatorContext arg1) {
  9. // TODO Auto-generated method stub
  10. //在这里我们就可以通过当前ConstraintValidator的成员变量minValue访问到当前限制类型Min的value属性了
  11. return value >= minValue;
  12. }
  13. }

 

       继续来说一下ConstraintValidator泛型的第二个类型,我们已经知道它的第二个类型是对应的isValid的方法的第一个参数,从我给的参数名称value来看也可以知道isValid方法的第一个参数正是对应的当前需要校验的数据的值,而它的类型也正是对应的我们需要校验的数据的数据类型。这两者的数据类型必须保持一致,否则Spring会提示找不到对应数据类型的ConstraintValidator。建立了自己的限制类型及其对应的ConstraintValidator后,其用法跟标准的JSR-303限制类型是一样的。以下就是使用了上述自己定义的JSR-303限制类型——Money限制和Min限制的一个实体类:

Java代码  
  1. public class User {
  2. private int age;
  3. private Double salary;
  4. @Min(value=8, message="年龄不能小于8岁")
  5. public int getAge() {
  6. return age;
  7. }
  8. public void setAge(int age) {
  9. this.age = age;
  10. }
  11. @Money(message="标准的金额形式为xxx.xx")
  12. public Double getSalary() {
  13. return salary;
  14. }
  15. public void setSalary(Double salary) {
  16. this.salary = salary;
  17. }
  18. }

另外再讲一点Spring对自定义JSR-303限制类型支持的新特性,那就是Spring支持往ConstraintValidator里面注入bean对象。现在假设我们在MoneyValidator里面需要用到Spring ApplicationContext容器中的一个UserController bean对象,那么我们可以给ConstraintValidator定义一个UserController属性,并给定其set方法,在set方法上加注解@Resource或@Autowired通过set方式来注入当前的ApplicationContext中拥有的UserController bean对象。关于@Resource和@AutoWired的区别可以参考这篇博客。所以我们可以这样来定义我们的MoneyValidator:

Java代码  
  1. public class MoneyValidator implements ConstraintValidator<Money, Double> {
  2. private String moneyReg = "^\\d+(\\.\\d{1,2})?$";//表示金额的正则表达式
  3. private Pattern moneyPattern = Pattern.compile(moneyReg);
  4. private UserController controller;
  5. public void initialize(Money money) {
  6. // TODO Auto-generated method stub
  7. }
  8. public boolean isValid(Double value, ConstraintValidatorContext arg1) {
  9. // TODO Auto-generated method stub
  10. System.out.println("UserController: .............." + controller);
  11. if (value == null)
  12. returntrue;
  13. return moneyPattern.matcher(value.toString()).matches();
  14. }
  15. public UserController getController() {
  16. return controller;
  17. }
  18. @Resource
  19. public void setController(UserController controller) {
  20. this.controller = controller;
  21. }
  22. }

SpringMVC之使用Validator接口进行验证相关推荐

  1. SpringMVC学习记录--Validator验证分析

    一.基于Validator接口的验证. 首先创建User实例,并加入几个属性 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ...

  2. Spring MVC验证器:Validator接口和ValidationUtils类

    本节主要介绍创建自定义 Spring 验证器时需要实现的 Validator 接口和工具类 ValidationUtils. Validator接口 创建自定义 Spring 验证器需要实现 org. ...

  3. springmvc开发微信公众号接口 微信公众号测试账号配置接口Token验证

    转:springmvc开发微信公众号接口 微信公众号测试账号配置接口Token验证 开发前必读以及准备工作: 1.微信公众平台开发是指为微信公众号进行业务开发,为移动应用.PC端网站.公众号第三方平台 ...

  4. SpringMVC集成Hibernate Validator进行注解式的参数校验——让代码更少、更加专注于业务逻辑

    SpringMVC集成Hibernate Validator进行注解式的参数校验 --让代码更少.更加专注于业务逻辑 1 问题背景: 参数验证是一个常见的问题,例如验证用户输入的密码是否为空.邮箱是否 ...

  5. 使用Spring的Validator接口进行校验

    你可以使用Spring提供的validator接口进行对象的校验.Validator接口与Errors协同工作,在Spring做校验的时候,它会将所有的校验错误汇总到Errors对象中去. 来看这个简 ...

  6. php接口开发 安全_PHP开发api接口安全验证的实例讲解

    php的api接口 在实际工作中,使用PHP写api接口是经常做的,PHP写好接口后,前台就可以通过链接获取接口提供的数据,而返回的数据一般分为两种情况,xml和json,在这个过程中,服务器并不知道 ...

  7. h5开启摄像头拍照+腾讯云接口人脸验证

    前端开启摄像头并拍照将照片的base64码传到后端 <div> //登录弹出框 <el-form ref="form" :model="form&quo ...

  8. 6U CPCI平台学习资料第116篇:基于5VLX110T FPGA FMC接口功能验证6U CPCI平台

    基于5VLX110T FPGA FMC接口功能验证6U CPCI平台 一.板卡概述 本板卡是Xilinx公司芯片V5系列芯片设计信号处理板卡.由一片Xilinx公司的XC5VLX110T-1FF113 ...

  9. 26:第三章:开发通行证服务:9:【注册/登录】接口:验证码校验OK后,先根据手机号去查查该用户是否已存在,如果用户不存在就创建这个用户;(tkmybatis查询构建查询条件,雪花算法,枚举类等等)

    说明: (1)本篇博客内容:继续开发[注册/登录]接口: ● 在[25:第三章:开发通行证服务:8:[注册/登录]接口:接收并校验"手机号和验证码"参数:]中,[注册/登录]接口, ...

最新文章

  1. 计算机二级的考试c模板,2013年9月全国计算机等级《二级C++》上机模考试卷(5)
  2. linux 查看某一个进程的socket连接数
  3. QT绘图控件QWT的安装及配置
  4. 怎么简述计算机网络的含义,计算机网络复习资料-含简答题参考答案
  5. 2017计算机发展,【2017年整理】计算机发展的四个阶段.doc
  6. Oracle11.2.0.4 RAC安装文档
  7. 每日一题(27)—— define定义一个宏表明1年中有多少秒
  8. 再会迪杰斯特拉(Dijkstra)
  9. probie 菜鸟翻译工具开源了
  10. 10款推荐系统模拟器汇总
  11. 区块链的20种应用场景
  12. Win7 DNS解析不准
  13. 购物车的制作 与注册表单验证
  14. 从南极到你家,易开得,一支“中国芯”的奇幻漂流
  15. Linux dnf使用记录
  16. 【android逆向笔记】(二)滚动的天空逆向
  17. html 页面新窗口打开,HTML 在新窗口打开全站链接
  18. Android逆向之旅---应用的反调试方案解析(附加修改IDA调试端口和修改内核信息)
  19. 来自雪域高原的馈赠——海拔2000米的大凉山高原生态糖心苹果
  20. PKU MOOC作业魔兽世界之一:备战的结构化程序

热门文章

  1. 2019最强就业指南,看了达摩院十大科技趋势,再也不怕互联网寒冬
  2. 程序实现:由给定几个数确定凸组合系数,组成一个给定的数
  3. 机器学习驱动技术是生物学进步的下一个突破
  4. IBM 揭晓全球第一项 2纳米芯片技术,为半导体领域实现重大突破
  5. 未来智能:人有人用,机有机用
  6. 麻省理工学院让软体机器人拥有了更好的触觉和空间感知能力
  7. RISC-V会彻底改变计算吗?
  8. 装配式建筑连入自动驾驶技术,未来城市的房子居然是这个样子......
  9. 10家最具创新性的机器学习公司
  10. 德国科学院院士:传统企业寻求变革,向智能制造靠近