Spring MVC(4):数据校验

Spring MVC数据校验


一般来说,准许开发中的 DRY 原则,对于Java程序中的数据校验逻辑和相应的域模型会进行绑定,将代码逻辑集中管理;
Spring 在使用DataBinder 对数据进行绑定时,同时可以调用相应的框架进行数据校验工作,提供了 org.springframwork.validation 包用于支持数据校验,其中核心接口为 Validate,支持自己提供的数据校验框架,同时提供一个工厂类 LocaleValidatorFactoryBean 同时支持自身的 Validate 接口、和 JSR-303 的 Validate 接口;


启用 Spring-mvc 数据校验功能

在 spring-mvc 上下文配置中装配一个  LocaleValidatorFactoryBean ,或者直接使用 即可(会自动装配一个 LocaleValidatorFactoryBean):
 
<mvc:annotation-driven  />
相当于以下:
 
<bean id="validatorFactoryBean" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<mvc:annotation-driven validator="validatorFactoryBean"/>

数据校验注解

在Spring-mvc中可以直接使用   JSR-303  的校验注解:
@Null / @NotNull 被注解的元素必须为null / 必须不为null
@AssertTrue / @AssertFalse 被注解的元素必须为true / 必须为false
@Min(value) / @Max(value) 被注解的元素必须大于等于指定最小值 / 小于等于指定最大值
@DecimalMin(value) / @DecimalMax(value) 同上,同时被注解元素必须为一个数字
@Size(max,min) 被注解的元素必须在指定范围内
@Digits(integer,fraction) 同上,被注解的元素必须是一个数字
@Past / @Future 被注解的元素必须是一个过去的时间 / 必须是一个将来的时间
@Email 被注解的元素必须是一个Email格式
@Pattern(regexp)  被注解的元素必须通过正则表达式regexp的验证,该元素必须是一个String
hibenate validator 对于 JSR-303 提供了一个良好的实现,同时还支持了以下的拓展注解:
@NotEmpty 被注解的元素必须不为空
@Length(min,max) 被注解的元素长度必须在指定范围内,一般为 String 类型
@Range 被注解的元素必须在核实范围内;

使用JSR-303 的数据验证注解,需要引入: javax.validation:validation-api 依赖;
使用 hibernate validator 的数据验证注解,需要导入:org.hibernate:hibernate-validator 依赖;



基于注解的数据校验的基本过程

示例代码模块:
site.assad.domain.User(领域对象)
site.assad.dao.UserDao(dao对象)
site.assad.service.UserService(service对象)
site.assad.web.UserController(控制器对象)
site/assad/applicationContext.xml(spring 配置文件)
webapp/assad-servlet.xml(spring mvc 配置文件)
webapp/web.xml
webapp/WEB-INF/views/user/jsp/*(登陆相关的 jsp 页面)

在领域对象使用注解验证注解
以下是在一个领域对象中,使用注解进行属性的校验注解:
 
package site.assad.domain;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.*;
public class User {
    private int userId;
    
    @NotNull(message="用户名不可为空")
    @Pattern(regexp = "\\S{2,15}",message="用户名必须包含2-15个非空字符")
    private String userName;
    @NotNull(message="用户密码不可为空")
    @Pattern(regexp = "\\w{6,30}",message="密码必须包含6-30个有字符,下划线,数字组成的字符")
    private String password;
    @Email(message="邮箱格式错误")
    private String email;
    @Past(message="必须是一个过去的日期")
    @DateTimeFormat(pattern = "yyyy-MM-dd")   //数据格式化
    private Date birthday;
    
    //省略getter,setter
}

在控制器使用@valid绑定校验模型 
在 Spring MVC 控制器的处理方法中,使用 @Valid 让Spring-mvc在数据绑定后进行数据校验,校验的结果会被绑定到 BindingResult 对象中,可以通过该对象获取校验结果的一些信息,常用的方法接口如下:
FieldError getFieldError(String field) 根据属性名获取对应的校验错误
List getFieldErrors() 获取所有的校验错误
Object getFieldValue(String field) 获取属性值
int getErrorCount() 获取错误数量
boolean hasError()  判断是否含有校验错误
 
//响应 “/register”请求,将数据绑定到“user”模型后,进行数据校验,将校验结果绑定到 BindingResult对象中
@RequestMapping("/handleRegister")
public String register( @Valid @ModelAttribute("user") User user,BindingResult bindingResult){
    if(bindingResult.hasErrors())
        return "/user/register";
    else
        return "/user/showDetail";
}

在JSP页面输出验证错误信息
在JSP中获取校验结果,由于校验错误信息是包含在隐含模型中的,隐含模型又存放在 HttpServletRequest 中,可以使用Spring 提供的
 标签来绑定该模型,通过获取验证结果:
 
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
    <title>用户注册title>
    <style> .error{color:darkred;} style>
head>
<body>
<jsp:useBean id="user" class="site.assad.domain.User" scope="request" />
<form:form action="/user/handleRegister" method="post" modelAttribute="user">
    
    用户名:<form:input path="userName" /><br/>
    <form:errors path="userName" cssClass="error"/><br /> 
    密码:<form:input path="password" /><br/>
    <form:errors path="password"/><br />
    Email:<form:input path="email" cssClass="error" /><br/>
    <form:errors path="email"/><br />
    生日(格式"yyyy-MM-dd"):<form:input path="birthday" /><br/>
    <form:errors path="birthday" cssClass="error"/><br />
    <input type="submit" value="提交" />
    <input type="reset" value="重置" /><br/>
form:form>
body>
html>





数据校验信息的国际化


以上示例中,校验的错误信息是硬编码在领域对象中的,为了解决 i18n 问题,可以将这些信息出储存在 i18n 资源中,以下是示例代码模块:
site.assad.domain.User(领域对象)
site.assad.dao.UserDao(dao对象)
site.assad.service.UserService(service对象)
site.assad.web.UserController(控制器对象)
site/assad/applicationContext.xml(spring 配置文件)
webapp/assad-servlet.xml(spring mvc 配置文件)
webapp/i18n/message.properties(i18n 资源文件)
webapp/web.xml
webapp/WEB-INF/views/user/jsp/*(登陆相关的 jsp 页面)

首先创建资源文件 webapp/i18n/message.properties如下:
 
#标准验证标签信息
register.userName.notNull=用户名必须不为空
register.userName.pattern=用户名必须包含2-15个非空字符
register.password.pattern=密码必须包含6-30个有字符,下划线,数字组成的字符
register.email=邮箱格式错误
register.birthday.past=必须是一个过去的日期
在Spring mvc上下文中装配i18n资源,绑定校验器工厂,assad-servlet.xml
 
 version="1.0" encoding="UTF-8"?>
<beans ....>
    
    ...
    
     ...
    
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
          p:defaultEncoding="UTF-8"
          p:basename="i18n/message"/>
    
    <bean id="validatorFactoryBean" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
          p:validationMessageSource-ref="messageSource" />
    
    <mvc:annotation-driven validator="validatorFactoryBean"/>
beans>
更改领域对象中的校验错误信息为相应的 i18n 资源:
 
public class User {
    private int userId;
    @NotNull(message="{register.userName.notNull}")
    @Pattern(regexp = "\\S{2,15}",message="{register.userName.pattern}")
    private String userName;
    @Pattern(regexp = "\\w{6,30}",message="{register.password.pattern}")
    private String password;
    @Email(message="{register.email}")
    private String email;
    @Past(message="{register.birthday.past}")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
    
    //省略 getter,setter
}





编写自定义数据校验代码

以上的过程都是基于Spring已经提供的数据校验标签来进行数据校验的,有几种主要的方法:

1)直接在处理方法中进行数据校验

可以直接在控制器处理方法代码块中,获取绑定对象,对绑定对象的属性进行数据校验,再将结果重新绑定到 BindingResult 或 Errors 对象中可以通过以下2种方法绑定校验错误信息:
 
BindingResult#rejectValue(String field,String errorCode) //向BindingResult对象注入值
ValidationUtils.rejectIfEmptyOrWhitespace(BindingResult bindingResult,String field,String errorCode)  //通过校验器工具向 Bindingresult 注入值,同时提供了空值检验的便捷方法
如以下代码对注册表单提交的绑定对象user的userName进行校验,如果数据库中存在该userName,校验失败:
 
package site.assad.web;
@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    //只负责路由转换
    @RequestMapping("/register")
    public String register(){
        return "/user/jsp/register";
    }
    //相应表单提交页面的路由请求,进行数据校验;
    @RequestMapping(value="/handleRegister",method=RequestMethod.POST)
    public String register1(@Valid @ModelAttribute("user") User user, BindingResult bindingResult,Model model){
        if(bindingResult.hasErrors()){
            return "/user/jsp/register";
        }
        boolean flag = userService.registerUser(user);
        if(flag){
            user = userService.getUserByName(user.getUserName());
            model.addAttribute("user",user);
            return "/user/jsp/userDetail";
        }else{
             //当userName在数据库中已经存在,返回注册页面,同时向 BindingResult 注入“userName”的校验失败信息
            bindingResult.rejectValue("userName","repeated"); 
            return "/user/jsp/register";
        }
    }
}
其中  bindingResult.rejectValue("userName","repeated");  即向 BindingResult 对象的 userName 注入一个值,该值的 i18n 错误码为 “repeated”,包括绑定资源文件中的以下键值:
  • repeated.user.userName
  • repeated.userName
  • repeated.java.lang.String
  • repeated
BindingResult 会按照以上顺序在对应的资源文件中查找值,直到第一个找到的值;

以下是对应的资源文件: webapp/i18n/message.properties
 
#自定义验证信息
repeated.userName=用户名已经存在

同时需要在 Spring mvc上下文中配置 i18n 资源:
 
 
 <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
        p:defaultEncoding="UTF-8"
        p:basename="i18n/message"/>
 <mvc:annotation-driven />



2)通过自定义实现的校验器

以上方式是直接在处理方法中编写校验逻辑,但对于某个command对象含有比较复杂的校验逻辑,可以把这些代码抽取出来,通过继承Validator接口,实现该command对象的自定义校验器,以维持代码的分层;

实现 User 对象的校验器UserValidator:
 
package site.assad.web.validator;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
//针对 User 的校验器
public class UserValidator implements Validator {
    @AutoWired
    private UserService userService;
    
    //判断需要需要校验的类
    public boolean supports(Class clazz) {
        return User.class.equals(clazz);
    }
    //校验逻辑
    public void validate(Object target, Errors errors) {
        if (target instanceof User) {
            User user = (User) target;
            if (userService.getUserByName(user).getUserId != 0) {
                errors.rejectValue("userName", "repeated");     //向错误对象中写入i18n资源中对应的错误码,同上
            }
        }
    }
}

控制器中 UserController 中绑定该自定义校验器
 
public class UserController{
    @InitBinder
    public void initBinder(WebDataBinder binder){
            binder.setValidator(new UserValidator());   //在进行数据绑定是使用自定义校验器
    }
    
    @RequestMapping("/handleRegister")
    public String register( @Valid @ModelAttribute("user") User user,BindingResult bindingResult,Model model){
        if(bindingResult.hasErrors()){
            return "/user/register";
        }else{
            user = userService.getUserByName(user.getUserName());
            model.addAttribute("user",user);
            return "/user/showDetail";
        }
    }
}

以上仅仅是在 UserController 控制器中局部启用自定义校验器 UserValidator,如果要在全局启用该校验器,可以在spring mvc上下文如如下配置:
 
<bean id="userValidator" class="site.assad.validator.UserValidator"/>
<mvc:annotation-driven validator="userValidator"/>




你可能感兴趣的:(Spring4,Spring,/,Spring,MVC,学习小结)