SpringMVC(五)数据验证
参数转换出来后,紧跟着往往是需要验证参数的合法性,因此SpringMVC也提供饿了验证参数的机制。一方面,SpringMVC支持JSR-303注解验证,在默认情况看下Spring Boot会引入关于Hibernate Validator
机制来支持JSR-303验证规范;另外一方面,因为业务会比较复杂,所以需要自定义验证规则。
JSR-303验证
JSR-303主要是通过注解的方式进行的。这里先定义一个需要验证的POJO,此时需要在其属性中加入相关的注解。
ValidatorPojo
package com.lay.mvc.pojo;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.*;
import java.util.Date;
/**
* @Description:
* @Author: lay
* @Date: Created in 17:12 2018/11/13
* @Modified By:IntelliJ IDEA
*/
public class ValidatorPojo {
//非空判断
@NotNull(message = "id不能为空")
private Long id;
//只能是将来日期
@Future(message = "需要一个将来日期")
//@Past //只能是过去的日期
@NotNull
private Date date;
@NotNull //不能为空
@DecimalMin(value = "0.1")//最小值0.1
@DecimalMax(value = "10000.00")//最大值10000.00
private Double doubleValue = null;
@NotNull //不能为空
@Min(value = 1,message = "最小值为1") //最小值为1
@Max(value = 88,message = "最大值为88") //最大值为88
private Integer integer;
@Range(min=1,max=888,message = "范围为1到888")//限定范围
private Long range;
@Email(message = "邮箱格式错误") //邮箱验证
private String email;
@Size(min=20,max=30,message = "字符串长度要求20到30之间")
private String size;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Double getDoubleValue() {
return doubleValue;
}
public void setDoubleValue(Double doubleValue) {
this.doubleValue = doubleValue;
}
public Integer getInteger() {
return integer;
}
public void setInteger(Integer integer) {
this.integer = integer;
}
public Long getRange() {
return range;
}
public void setRange(Long range) {
this.range = range;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
}
为了测试验证器,编写一个前台页面,然后使用JSON的数据请求发送这个对象给控制器。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta charset="UTF-8">
<title>测试JSR-303</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
//请求验证的POJO
var pojo = {
id: null,
date: '2018-11-13',
doubleValue: 99999.09,
integer: 100,
range: 1000,
email: 'email',
size: 'testchangdu',
regexp: 'a,b,c,d'
}
$.ajax({
// 请求地址
url: "../valid/validate",
type:"post",
//告知传递参数类型为JSON
contentType:"application/json",
//将JSON转为字符串传递
data: JSON.stringify(pojo),
//成功后的方法
success: function (result) {
alert("success");
}
});
});
</script>
</head>
<body>
</body>
</html>
在页面初始就使用Ajax发送Json请求。
控制器
package com.lay.mvc.controller;
import com.lay.mvc.pojo.ValidatorPojo;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Description:
* @Author: lay
* @Date: Created in 17:33 2018/11/13
* @Modified By:IntelliJ IDEA
*/
@Controller
public class ValidController {
/**
*
* @Description: 验证页面
* @param: []
* @return: java.lang.String
* @auther: lay
* @date: 17:34 2018/11/13
*/
@GetMapping("/valid/page")
public String validPage(){
return "validator/pojo";
}
/**
*
* @Description: 解析验证参数错误
* @param: validatorPojo 需要验证的POJO,使用注解@Valid表示验证
* @param errors 错误信息,它由Spring MVC通过验证POJO后自动填充
* @return: java.util.Map<java.lang.String,java.lang.Object> 错误信息
* @auther: lay
* @date: 17:43 2018/11/13
*/
@RequestMapping(value ="/valid/validate")
@ResponseBody
public Map<String,Object> validate(
@Valid @RequestBody ValidatorPojo validatorPojo, Errors errors){
Map<String,Object> errMap=new HashMap<>();
//获取错误列表
List<ObjectError> oes=errors.getAllErrors();
for(ObjectError oe:oes){
String key=null;
String msg=null;
//字段错误
if(oe instanceof FieldError){
FieldError fe= (FieldError) oe;
key=fe.getField();//获取错误验证字段名
}else {
//非字段错误
key=oe.getObjectName();//获取验证对象名称
}
//错误信息
msg=oe.getDefaultMessage();
errMap.put(key,msg);
}
return errMap;
}
}
浏览器输入:http://localhost:8080/valid/page,
然后F12打开 控制台——Network(网络)——XHR(查看异步请求)——Response(返回),就会看到下面的json数据集
{
"date": "需要一个将来日期",
"doubleValue": "必须小于或等于10000.00",
"email": "邮箱格式错误",
"id": "id不能为空",
"integer": "最大值为88",
"range": "范围为1到888",
"size": "字符串长度要求20到30之间"
}
参数验证机制
为了能够更加灵活地提供验证机制,Spring孩童了自己的验证机制。在参数转换时,可以看到在SpringMVC中,存在WebDataBinder
机制进行管理,在默认情况下Spring会自动地根据上下文通过注册了的转换器转你出控制器所需要的参数。除了可以注册转换器外,还允许注册验证器(Validator)。
在Spring控制器中,它还允许使用注解@InitBinder
,这个注解的作用是允许在进入控制器的方法前修改WebDataBinder
机制。
Validator接口源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.validation;
public interface Validator {
/**
* 判定当前验证器是否支持该Class类型的验证
* @param clazz pojo类型
* @return 当前验证器是否支持该POJO验证
*/
boolean supports(Class<?> var1);
/**
* 如果supports返回true,则这个方法执行验证逻辑
* @param target 被验证的POJO对象(已绑定参数)
* @param errors 错误对象
*/
void validate(Object target, Errors errors);
}
它定义了两个方法,其中supports方法参数为需要验证的POJO类型,如果该方法返回true,则Spring会使用当前验证器的validate方法去验证pojo,而validate方法包含需要的target对象和错误对象errors,其中target是绑定参数后的pojo,这样便可以通过这个参数对象进行业务逻辑的自定义验证。如果发现有错误,则可以保存到errors对象中,然后返回给控制器。
自定义用户验证器
PersonValidator
package com.lay.mvc.validator;
import com.lay.mvc.entity.Person;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
/**
* @Description:
* @Author: lay
* @Date: Created in 18:14 2018/11/13
* @Modified By:IntelliJ IDEA
*/
public class PersonValidator implements Validator {
//该验证器支持Person类的验证
@Override
public boolean supports(Class<?> clazz) {
return clazz.equals(Person.class);
}
//验证逻辑
@Override
public void validate(Object target, Errors errors) {
//对象为空
if(target==null){
//直接在参数处报错,这样就不能进入控制器的方法
errors.rejectValue("",null,"用户不能为空");
return;
}
//强制转换
Person person=(Person)target;
//用户名非空串
if(StringUtils.isEmpty(person.getPersonName())){
//增加错误,可以进入控制器方法
errors.rejectValue("personName","null","用户名不能为空");
}
}
}
有了这个验证器,Spring还不会自动启用它,因为还美誉哦绑定给WebDataBinder
机制。在Spring MVC中提供了一个注解@InitBinder
,它的作用是在执行控制器的方法前,处理器会先执行@InitBinder
标注的方法。这时可以将WebDataBinder
对象作为参数传递到方法中,通过这层关系得到WebDataBinder
对象,这个对象有个setValidator
方法,它可以绑定自定义的验证器,这样就可以在获取参数之后,通过自定义的验证器去验证参数,只是WebDataBinder
除了可以绑定验证器外,还可以进行参数的自定义,例如,不使用@DataTimeFormat
获取日期参数。
控制器
/**
*
* @Description:调用控制器前执行这个方法
* @param: [webDataBinder]
* @return: void
* @auther: lay
* @date: 18:39 2018/11/13
*/
@InitBinder
public void initBinder(WebDataBinder webDataBinder){
//绑定验证器
webDataBinder.setValidator(new PersonValidator());
//定义日期格式参数,参数不再需要注解@DateTimeFormat,boolean参数表示是否允许为空
webDataBinder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),false));
}
/**
*
* @Description: 解析验证参数错误
* @param: validatorPojo 需要验证的POJO,使用注解@Valid表示验证
* @param errors 错误信息,它由Spring MVC通过验证POJO后自动填充
* @return: java.util.Map<java.lang.String,java.lang.Object> 错误信息
* @auther: lay
* @date: 17:43 2018/11/13
*/
@RequestMapping(value ="/valid/personValid")
@ResponseBody
public Map<String,Object> personValid(
@Valid Person person, Errors errors,Date date){
Map<String,Object> map=new HashMap<>();
map.put("person",person);
map.put("date",date);
//获取错误列表
List<ObjectError> oes=errors.getAllErrors();
for(ObjectError oe:oes){
String key=null;
String msg=null;
//字段错误
if(oe instanceof FieldError){
FieldError fe= (FieldError) oe;
key=fe.getField();//获取错误验证字段名
}else {
//非字段错误
key=oe.getObjectName();//获取验证对象名称
}
//错误信息
msg=oe.getDefaultMessage();
map.put(key,msg);
}
return map;
}
浏览器输入:http://localhost:8080/valid/personValid?person=1--note_1&date=2018-11-13
这里userName为空。
结果
{
"date": "2018-11-12T16:00:00.000+0000",
"person": {
"id": 1,
"note": "note_1",
"personName": ""
},
"personName": "用户名不能为空"
}