Spring MVC使用篇(十一)—— Validation数据校验

1、综述

  对于MVC模式的Web应用,不同层面校验的机制是不同的。例如在Controller控制层,会校验页面请求参数的合法性;在Service业务层,主要校验关键业务参数,且仅限于Service接口中使用的参数。而对于DAO数据交互层,一般不需要再进行校验。

  在后端校验的都是比较常用的数据,绝大部分业务逻辑都使用一样的校验方法,这时候如果在每个方法里面都添加这种校验逻辑,会是大妈臃肿不好维护,因此需要将校验逻辑集中起来。Spring MVC为此提供了多种校验机制,其中有Bean Validation及Spring Validator接口校验。在Spring 4.0之后,支持Bean Validation 1.0(JRS-303)和Bean Validation 1.1(JRS-349)校验,可以单独集成Hibernate的validation校验框架,用于服务端的数据校验。

2、Bean Validation 数据校验

2.1 导入jar包

  要在项目中使用Bean Validation校验机制,需要添加相关jar包,具体如下图所示:
在这里插入图片描述
在这里插入图片描述
  其中validation-api.jar提供了Bean Validation的基本校验机制,以及用于xml文档验证的api。hibernate-Validator.jar是Hibernate的一个验证框架,其中包含了Bean Validation的检验约束拓展,不需要和Hibernate的其他部分绑定就可以使用。即使项目中没有使用Hibernate,但是使用了Hibernate的validation机制,就要引入Hibernate的依赖jar包jboss-logging.jar。

  Bean Validation校验框架的一个重要特性就是,检测实体封装类JavaBean中的数据。 它会使用简洁的注解语法来对Bean中的某个属性进行校验。

2.2 搭建validation校验框架

  在前面的用户登录项目基础上,实现真正对用户通过表单提交过来的数据进行校验。

  login.jsp页面为:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>   
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
<html>  
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>用户登录</title>
    </head>
    <body>
        <h3>用户登录</h3>
        <form action="doLogin.action" method="post">
            <table width="300px;" border=1>
                <tr>
                    <td>用户编号:</td>
                    <td><input type="text" name="userId" id="userId" /></td>
                </tr>
                <tr>
                    <td>用户名:</td>
                    <td><input type="text" name="username" id="username" /></td>
                </tr>
                <tr>
                    <td>密 码:</td>
                    <td><input type="password" name="password" id="password" /></td>
                </tr>
            </table>
            <br/>
            <input type="submit" id="login_button" value="用户登录" />
        </form>
    </body>
</html> 

  当用户的登录信息处理完毕后,如果校验成功,向用户显示success.jsp页面,提示登录成功,具体页面代码如下所示:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>登录成功</title>
</head>
<body>
  <h2>登录成功!</br></br>欢迎您${user.username}</h2>
  <hr/>
  <h3>使用user参数显示用户注册信息</h3>
  <table width="300px;" border=1>
    <tr>
      <td>用户编号:</td>
      <td>${user.userId}</td>
    </tr>
    <tr>
      <td>用户名:</td>
      <td>${user.username}</td>
    </tr>
    <tr>
      <td>密 码:</td>
      <td>${user.password}</td>
    </tr>
  </table>
</body>
</html>

  当用户的登录信息处理完毕后,如果校验失败,向用户显示error.jsp页面,提示登录失败,并显示具体校验错误信息,具体页面代码如下所示:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: xiaobaixiaoda
  Date: 2018/11/14
  Time: 14:47
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录错误</title>
</head>
<body>
    <h1>登录错误!</h1>
    <c:if test="${allErrors != null}">
        <c:forEach items="${allErrors}" var="error">
            <font color="red">${error.defaultMessage}</font><br/>
        </c:forEach>
    </c:if>
</body>
</html>

  要搭建validation校验框架,由于该校验机制是给处理器Controller使用的,而加载和调用处理器的是处理器适配器HandlerAdapter,所以要为处理器适配器的配置添加校验器。首先在核心配置文件springmvc.xml中的annotation-driven的注解驱动配置上添加一个Validator属性,为其指定一个“Validator”值,该值为“校验器”的名称。具体配置如下:

 <!--配置基于注解的处理器适配器与处理器映射器-->
 <mvc:annotation-driven conversion-service="conversionService" validator="validator"/>

  然后需要在核心配置文件springmvc.xml中添加名为“Validator”的校验器配置,具体配置如下:

<!--配置校验器-->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
    <property name="validationMessageSource" ref="messageSource" />
</bean>

  这里定义了一个id为“validator”的校验器,指定其中的校验器提供类是“HibernateValidator”,即添加的Hibernate校验器。而下面的validationMessageSource指的是校验使用的资源文件,在该文件中配置检验的错误信息。如果不手动指定检验资源文件,则会默认使用classpath下的ValidationMessage.properties。

  接下来,自定义了validationMessageSource对应的错误信息配置文件,在核心配置文件中添加id为messageSource的资源属性文件配置,具体配置如下:

<!--校验错误信息配置-->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>classpath:LoginValidationMessage</value>
        </list>
    </property>
    <property name="fileEncodings" value="utf-8" />
    <property name="cacheSeconds" value="120" />
</bean>

  加载错误信息定义的资源文件的类为ReloadableResourceBundleMessageSource,需要为其指定资源文件名beannames、资源文件编码格式fileEncodings,以及资源文件内容的缓存时间cacheSeconds(单位为s)。可以在beannames配置中添加多个资源文件的配置信息,这里添加的是classpath下的名为“LoginValidationMessage”的属性文件。文件编码格式设置为“utf-8”,而内容缓存时间设置为120s。

  最后,整体的核心配置文件配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/mvc
		http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
		http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
		
    <!--配置基于注解的处理器适配器与处理器映射器-->
    <mvc:annotation-driven conversion-service="conversionService" validator="validator"/>

    <!--配置校验器-->
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
        <property name="validationMessageSource" ref="messageSource" />
    </bean>
    
    <!--校验错误信息配置-->
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>classpath:ProductValidationMessage</value>
            </list>
        </property>
        <property name="fileEncodings" value="utf-8" />
        <property name="cacheSeconds" value="120" />
    </bean>

    <!--使用扫描配置,对某一个包下面的所有类进行扫描,
    找出所有使用@Controller注解的Handler控制器类-->
    <context:component-scan base-package="com.ccff.controller"/>

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
        <!--<property name="order" value="2"/>-->
    </bean>

    <!--注册类型转换器-->
    <bean id="dateConverter" class="com.ccff.convert.DateConvert" />

    <!--注册类型转换服务Bean-->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters" ref ="dateConverter" />
    </bean>
</beans>

2.3 添加校验注解信息

  在用户登录的过程中,需要对用户输入的用户名和密码做校验,所以在User的JavaBean类上,对用户名和密码添加校验注解信息,代码如下:

package com.ccff.model;

import org.hibernate.validator.constraints.NotEmpty;

import javax.validation.constraints.Size;
import java.util.Date;

public class User {
    private int userId;
    @NotEmpty(message = "{user.username.isEmpty}")
    private String username;
    @Size(min = 6, max = 12, message = "{user.password.length.error}")
    private String password;
    private Date loginDate;

    public Date getLoginDate() {
        return loginDate;
    }

    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

  可以看到,在password属性上添加了@Size注解,并且制定了其中最小(min)和最大(max)字符限制,其中的message用来提示检验出错误时显示的错误信息。校验非空使用的注解为@NotEmpty,其中也指定了message错误信息。通过包的引入可以知道,分别使用了BeanValidation的约束及Hibernate Validation的拓展约束。

  由于我们在配置文件中声明的错误文件信息还没有创建,因此在config中创建名为“LoginValidationMessage.properties”的属性文件,用来配置校验错误信息。在该properties文件中添加上面注解中message指定的错误信息对应的中文显示信息即可,具体配置如下:

#添加校验错误提示信息
user.username.isEmpty=用户名不能为空,请输入用户名
user.password.length.error=密码的位数应该在6~12位

  接下来在Controller中的doLogin方法中捕捉校验信息,具体代码如下:

package com.ccff.controller;

import com.ccff.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller
@RequestMapping("/ValidationTest")
public class ValidationController {
    @RequestMapping("/login")
    public String login(){
        return "validation/login";
    }

    @RequestMapping("/doLogin")
    public String doLogin(@Validated User user, BindingResult bindingResult, Model model) {
        //获取检验错误信息
        List<ObjectError> allErrors = null;
        if (bindingResult.hasErrors()){
            allErrors = bindingResult.getAllErrors();        
            model.addAttribute("allErrors",allErrors);
            return "validation/error";
        }else {
            model.addAttribute("user",user);
            return "validation/success";
        }
    }
}

  这里,在Controller的doLogin方法形参user前面添加了@Validated注解,在后面添加了BindingResult类。一般会在需要校验的Bean形参前面加@Validated注解,标注该参数需要执行Validated校验,而在需要校验的Bean形参后面添加BindingResult参数接收校验的出错信息。

  这里需要注意,@Validated和BindingResult注解时成对出现的,并且在形参中出现的顺序是固定的(一前一后)。

2.4 测试validation校验效果

  将整个项目部署在Tomcat上后,在浏览器内输入请求URL:http://localhost:8080/demo/ValidationTest/login.action 后向用户显示登录界面,具体如下:
在这里插入图片描述
  在登录界面中,用户需要输入必要的登录信息,这里在后台Controller的doLogin方法中对用户名和密码做了校验(用户名不能为空,密码位数为6-12位)。

  首先测试对用户名的校验,在登录表单中输入如下信息:
在这里插入图片描述
  由上图可见,用户名为空,当表单提交后,则会提示登录错误,并显示用户名不能为空的校验错误信息:
在这里插入图片描述
  接下来测试对密码的校验,在登录表单中输入如下信息:
在这里插入图片描述
  由上图可见,输入的密码的位数为3位,不满足密码的校验条件,因此登录失败,并显示密码6-12位的校验错误信息:
在这里插入图片描述
  然后,对用户名和密码的校验进行共同测试,在登录表达中输入如下内容:
在这里插入图片描述
  由上图可见,对于用户名和密码的校验均出错,因此跳转到登录失败页面,并提示全部校验错误信息,结果如下:
在这里插入图片描述
  最后,当全部输入正确信息后,提交登录表单,则跳转到登录成功页面,显示登录用户信息:
在这里插入图片描述

2.5 validation注解全面介绍

  上面在Bean里面添加的注解信息,在Bean validation机制中叫做“constraint”约束,分为Bean validation本省的constraint及Hibernate提供的拓展constraint。上面的例子中使用了一部分constraint,全部constraint如表1、表2所示:

表1:Bean Validation中内置的constraint

constraint详细信息
@Null被注释的元素必须为null
@NotNull被注释的元素必须不为null
@AssertTrue被注释的元素必须为true
@AssertFlase被注释的元素必须为false
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max,min)被注释的元素的大小必须在指定的范围内
@Digits(integer,fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Future被注释的元素必须是一个将来的日期
@Pattern(value)被注释的元素必须符合指定的正则表达式

表2:Hibernate Validation附加的constraint

constraint详细信息
@Email被注释的元素必须是电子邮箱地址
@Length被注释的字符串的大小必须在指定的范围内
@NotEmpty被注释的字符串必须非空
@Range被注释的元素必须在合适的范围内

  以上两个表展示了Validation所有的constraint约束信息。一个constraint通常由一个annotation注解和一个相关的constraint validator约束校验器组成,它们是一对多关系,也即一个annotation会对应多个constraint validator约束校验器。当程序运行时,Bean Validation会根据被注释的元素的具体类型,选择合适的constraint validator约束校验器对数据进行校验。

  不同的constraint有不同的校验效果,在日常开发中,可以根据需要,在重要的字段上添加适当的constraint约束。

3、分组校验

  当使用Bean Validation校验框架的时候,一般都会将校验信息配置在对应的实体JavaBean中。这样一来,所有使用该实体类Bean的Controller类对应的方法都要进行一次校验。但是有些Controller仅仅将这个实体类作为查询条件,这样这个实体类再进行Bean Validation校验就会出问题,导致该查询方法抛出不该抛出的异常。

  即直接定义在JavaBean中的校验注解,需要满足当JavaBean被多个Controller所共用时,每个Controller方法对该JavaBean有不同的校验规则。

  在没有设置分组校验的JavaBean中,对所有Controller类的校验规则都是一致的。

  在Spring MVC中提供了“分组校验”的方式,将不同的校验规则分给不同的组,当在Controller方法中校验相关的实体类Bean时,可以指定不同的组使用不用的校验规则。

3.1 为登录案例设置分组校验

  首先,在创建名为“com.ccff.validator”的包,然后在该包下创建两个组接口,第一个校验组接口名为“UserValidationGroup1”,第二个校验组接口名为“UserValidationGroup2”。具体代码如下所示:

package com.ccff.validator;

//检验分组1
public interface UserValidationGroup1 {
    //接口中不需要定义任何方法,仅对不同的校验规则进行分组
}
package com.ccff.validator;

//检验分组2
public interface UserValidationGroup2 {
    //接口中不需要定义任何方法,仅对不同的校验规则进行分组
}

  值得注意的是,分组接口中不需要编写任何的方法定义,该接口仅仅作为分组校验的一个标识接口。

  然后,将User实体类中的两个校验分配给不同的组,具体代码如下:

package com.ccff.model;

import com.ccff.validator.UserValidationGroup1;
import com.ccff.validator.UserValidationGroup2;
import org.hibernate.validator.constraints.NotEmpty;

import javax.validation.constraints.Size;
import java.util.Date;

public class User {
    private int userId;
    @NotEmpty(message = "{user.username.isEmpty}", groups = {UserValidationGroup1.class})
    private String username;
    @Size(min = 6, max = 12, message = "{user.password.length.error}", groups = {UserValidationGroup2.class})
    private String password;
    private Date loginDate;

    public Date getLoginDate() {
        return loginDate;
    }

    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

  通过上面对于User实体JavaBean的校验修改,可以看到将用户名不能为空的校验指派给了UserValidationGroup1代表的组,而密码长度应该在6-12位的校验指派给了UserValidationGroup2代表的组。

  接下来在Controller中指定实体类校验规则所属组时,只需要在该实体类前面的@Validated注解中添加一个value即可,该value值指定校验规则所在的组接口,具体代码如下:

package com.ccff.controller;

import com.ccff.model.User;
import com.ccff.validator.UserValidationGroup1;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller
@RequestMapping("/ValidationTest")
public class ValidationController {
    @RequestMapping("/login")
    public String login(){
        return "validation/login";
    }

    @RequestMapping("/doLogin")
    public String doLogin(@Validated(value = UserValidationGroup1.class) User user, BindingResult bindingResult, Model model) {
        //获取检验错误信息
        List<ObjectError> allErrors = null;
        if (bindingResult.hasErrors()){
            allErrors = bindingResult.getAllErrors();
            for (ObjectError objectError : allErrors){
                //输出错误信息
                System.out.println(objectError.getDefaultMessage());
            }
            model.addAttribute("allErrors",allErrors);
            return "validation/error";
        }else {
            model.addAttribute("user",user);
            return "validation/success";
        }
    }
}

  通过上面的代码我们可以看出,此时我们将处理登录信息的doLogin方法中对形参user的校验设置了校验分组,分组为第一组,即只能校验用户名是否为空,而对于密码在6-12位的校验是不起作用的。

3.1 测试登录案例的分组校验

  将项目重新部署到Tomcat上后,在浏览器输入请求地址:http://localhost:8080/demo/ValidationTest/login.action 后显示登录界面,具体如下所示:
在这里插入图片描述
  当全部输入正确信息后,提交表单,则跳转到登录成功界面,显示登录的用户信息,具体如下所示:
在这里插入图片描述
  若在登录界面,只输入用户编号和密码,不输入用户名,提交表单后,由于在doLogin方法中对User实体类设置的校验规则分组为第一组,即能够校验用户名是否为空,因此跳转到登录失败界面,提示校验错误信息,具体如下:
在这里插入图片描述
  若在登录界面只输入用户编号和用户名,输入的密码位数为3位时,具体如下:
在这里插入图片描述
  此时提交表单后,由于对密码位数的校验属于校验分组的第二组,所以目前的Controller的doLogin方法将不会对密码的位数进行校验,因此仍然会跳转到登录成功界面,显示用户登录信息,具体如下所示:
在这里插入图片描述
  上面的测试结果说明指定的分组校验是成功的。当Controller仅需要对同一个实体Bean中的不同字段进行不同的校验时,可以使用分组校验来指派不同的校验规则,然后在不同的Controller方法参数中的@Validated注解中指定需要的校验规则所在的组接口即可。

4、Spring Validator数据校验

  除了上面讲述的Bean Validation校验机制外,Spring MVC还有自己的校验规则,即Validator接口校验。Spring MVC提供了一个Validator验证接口,可以使用它来验证自己定义的实体对象。该验证接口使用一个Errors对象工作,当验证器验证失败的时候,会向Errors对象填充验证失败的信息。

  Bean Validation是在需要校验的JavaBean中进行约束指定,而Spring的Validator接口校验是实现Validator接口,并编写指定类型的校验规则。

4.1 Validator接口的使用

  这里继续以之前的用户登录项目作为案例演示,对User实体类中的用户名和密码两个属性使用Spring MVC提供的Validator进行验证。

  首先,需要编写一个Validator接口的实现类,并实现Validator接口的supports方法和validate方法。其中supports方法主要用于判断当前的Validator实现类是否支持校验当前需要检验的实体类,如果支持,该方法返回true,此时才可以调用validate方法来对需要校验的实体类进行校验。

  在包com.ccff.validator下创建名为“UserValidator”的类,并实现Validator接口,具体代码如下:

package com.ccff.validator;

import com.ccff.model.User;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class UserValidator implements Validator {
    @Override
    public boolean supports(Class<?> aClass) {
        return User.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors) {
        ValidationUtils.rejectIfEmpty(errors,"username","Username.is.empty","用户名不能为空!!!");
        User user = (User) o;
        if (null == user.getPassword() || "".equals(user.getPassword())){
            //指定验证失败的字段名、错误码、默认错误信息
            errors.rejectValue("password","Password.is.empty","密码不能为空!!!");
        }else if (user.getPassword().length() < 6){
            //指定验证失败的字段名、错误码、默认错误信息
            errors.rejectValue("password","Password.length.too.short","密码至少为6位!!!");
        }else if (user.getPassword().length() > 12){
            //指定验证失败的字段名、错误码、默认错误信息
            errors.rejectValue("password","Password.length.too.long","密码最多为12位!!!");
        }
    }
}

  在上面的叫眼泪中,supports方法只对“com.ccff.model.User”类型的实体类进行校验。然后在validate方法中编写具体的校验逻辑,并根据不同的校验结果,将错误信息放入错误对象Errors中。

  在validate方法中使用了Errors错误对象的若干方法,而Errors是存储和暴露数据绑定错误和验证错误相关信息的接口,其提供了存储和获取错误消息的方法。以第一个rejectValue方法为例,设置了错误字段名为“password”,注册全局错误码为“Password.is.empty”,设置了默认消息“密码不能为空!!!”。

  在上面的User校验类中最初使用的ValidationUtils是Spring提供的一个校验工具类,第一个参数时错误信息要装入的Errors对象,然后紧跟着是错误的字段名称、全局错误码,最后设置一个错误参数,该参数在全局错误码中可以根据位置引入。

  然后,在定义好UserValidator之后,想要使用它,需要在Controller中的initBinder方法中为DataBinder设置一个Validator(即UserValidator),然后在相关方法的形参中添加BindingResult对象,当Controller通过UserValidator检测出错误时,会将错误防止在BindingResult对象的Errors属性中,通过其hasErrors()方法可以获知检测User对象中属性时是否异常。具体代码如下所示:

package com.ccff.controller;

import com.ccff.model.User;
import com.ccff.validator.UserValidationGroup1;
import com.ccff.validator.UserValidator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.validation.Valid;
import java.util.List;

@Controller
@RequestMapping("/ValidationTest")
public class ValidationController {
    @InitBinder
    public void initBinder(DataBinder binder){
        binder.setValidator(new UserValidator());
    }

    @RequestMapping("/login")
    public String login(){
        return "validation/login";
    }

    @RequestMapping("/doLogin")
    public String doLogin(@Valid User user, BindingResult bindingResult, Model model) {
        //获取检验错误信息
        List<ObjectError> allErrors = null;
        if (bindingResult.hasErrors()){
            allErrors = bindingResult.getAllErrors();        
            model.addAttribute("allErrors",allErrors);
            return "validation/error";
        }else {
            model.addAttribute("user",user);
            return "validation/success";
        }
    }
}

  上面的代码首先通过initBinder方法,为DataBinder对象设置Validator校验对象。当在执行“doLogin.action”请求的时候,doLogin方法的形参中要防止前面提到的BindingResult参数,它会将DataBinder中的Validator的校验结果中的错误对象,封装在自己的Errors对象集合中。在相关的方法中同样通过getAllErrors获得校验的所有异常信息。

  而在doLogin方法的User形参前,使用@Valid注解对其进行标注,这是因为只有当使用@Valid标注需要校验的参数时,Spring才会对其进行校验。而在校验的参数后面,必须给定一个包含Errors的参数,可以是Errors本身,也可以是其子类BindingResult。如果不设置包含Errors的参数,Spring会直接抛出异常,而设置后Spring会将异常的处理权交由开发人员,由开发人员来处理形参中包含Errors参数的对象。这里需要注意这个参数必须紧挨着@Valid注解标注的参数。

  最后,修改User实体类,将原有的验证注解删除,修改后的具体代码如下所示:

package com.ccff.model;

import java.util.Date;

public class User {
    private int userId;
    private String username;
    private String password;
    private Date loginDate;

    public Date getLoginDate() {
        return loginDate;
    }

    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

4.2 Validator接口验证测试

  第一步,将项目重新部署到Tomcat上后,在浏览器内输入请求URL:http://localhost:8080/demo/ValidationTest/login.action 后显示登录界面,如下图所示:
在这里插入图片描述
  由于之前已经对用户名不为空和密码的位数在6-12位做过了多次校验,此处测试我们仅测试对密码不为空的校验。在登录表单中输入如下内容,密码栏为空,具体如图所示:
在这里插入图片描述
  当表单提交后,Controller中的doLogin方法会通过UserValidator对用户信息进行校验,当校验出错误时,会将错误放置在BindingResult对象的Errors属性中。页面跳转到登录失败页面,并显示校验的错误信息。具体如下图所示:
在这里插入图片描述
  由测试结果可以看出,我们编写的Validator校验实现类起到了校验User的作用。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值