spring boot validate 统一处理

December 27, 2018

统一处理validate的校验

在开发业务系统的过程中都会设计到一些接口请求参数的校验,之前都是单个接口进行处理,现在介绍一种方法统一处理这个校验。

1、实现

通过继承ResponseEntityExceptionHandler这个类并实现handleMethodArgumentNotValid这个方法,就可以统一处理所以经过Valid注解过的接口,方法中的实现根据业务来,此处我写了一个统一返回类ResponeVo,主要用这个类和前端进行REST FULL交互,只要出现校验命中的就会来进入此方法执行,并且携带具体的错误信息;如下的处理逻辑是:返回和前端约定的固定codemsg字段用于返回校验时填入的信息,data中返回请求的值。

package com.demo.validatecommondemo.controller;

import com.alibaba.fastjson.JSON;
import com.demo.validatecommondemo.vo.ResponeVo;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice
@RestController
public class ValidateCommonHandler extends ResponseEntityExceptionHandler {


	@Override
	protected ResponseEntity<Object> handleMethodArgumentNotValid(
			MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status,
			WebRequest request) {
		ResponeVo vo = new ResponeVo();
		vo.setCode("400");
		for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) {
			String defaultMessage = fieldError.getDefaultMessage();
			vo.setMsg(defaultMessage);
			Object value = fieldError.getRejectedValue();
			String data = "请求参数值为:"+JSON.toJSONString(value);
			vo.setData(data);
			break;
		}
		return new ResponseEntity(vo, HttpStatus.OK);
	}
}

2、Controller类书写

如下的接口中addStudent添加了Valid注解。

package com.demo.validatecommondemo.controller;


import com.alibaba.fastjson.JSON;
import com.demo.validatecommondemo.entity.Girlfriend;
import com.demo.validatecommondemo.entity.Student;
import com.demo.validatecommondemo.vo.ResponeVo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

@RestController
public class DemoController {
    @PostMapping(path = "/student")
	public ResponeVo<Boolean> addStudent(@RequestBody @Valid Student student){
		studentsMap.put(student.getName(),student);
		return ResponeVo.create(true);
	}
}

解释如下的几个东西,这个类没有写getset方法,因为使用了@Data注解,更多功能请参考 官网,其他的呢就都是正常的注解,其中idCardphone因为没有官方的注解,所以通过正则表达式来进行匹配,这种方法也是支持的。然后就是backCard银行卡校验有点问题,感觉规则和中国的不太一样,所以最好是自己用正则去校验。

package com.demo.validatecommondemo.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.hibernate.validator.constraints.CreditCardNumber;
import org.hibernate.validator.constraints.Length;

import javax.validation.Valid;
import javax.validation.constraints.Email;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.util.Date;
import java.util.List;

@Data
public class Student {

	@Length(min = 1,message = "姓名不能为空")
	@NotNull(message = "姓名不能为空")
	private String name;

	@NotNull(message = "年龄不能为空")
	@Min(value = 18,message = "年龄不能小于18岁")
	@Max(value = 56,message = "年龄不能大于56岁")
	private Integer age;

	@NotNull(message = "邮箱不能为空")
	@Email(message = "邮箱地址无效")
	private String email;

	@NotEmpty(message = "身份证号不能为空")
	@Pattern(regexp = "^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X)$",message = "请输入正确的身份证号")
	private String idCard;

	@NotEmpty(message = "手机号码不能为空")
	@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "请输入正确的手机号码")
	private String phone;

	@NotNull(message = "请输入合法的时间")
	@Future(message = "请输入合法的时间")
	@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
	private Date inSchoolDate;

	@NotNull(message = "请输入正确的银行卡号")
	@CreditCardNumber(message = "请输入正确的银行卡号")
	private String backCard;

    //嵌套校验
    @NotEmpty(message = "请填写女朋友信息")
	private List<@Valid Girlfriend> girlfriends;

}
package com.demo.validatecommondemo.entity;

import lombok.Data;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.Digits;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@Data
public class Girlfriend {

	@NotEmpty(message = "请输入姓名")
	private String name;

	@NotNull(message = "请输入年龄")
	@Range(min = 18,max = 24,message = "对不起,年龄不在合适的范围内")
	private Integer age;
	@NotNull(message = "请输入性别")
	@Digits(integer = 1,fraction = 0,message = "只接受女性为女朋友")
	private Integer gender;
}

3、请求

请求参数:

{
	"name":"l",
	"age":18,
	"email":"1@qq.com",
	"phone":"77777777777",
	"idCard":"44444444444444444",
	"backCard":"44444",
	"inSchoolDate":"2019-10-10 10:10:10",
	"girlfriends":[
		{}
		]
}

响应参数:

{
    "code": "400",
    "msg": "请输入性别",
    "data": "请求参数值为:null"
}

如下说明校验生效了。

支持的校验标签

1、javax.validation 包下

meta-data comment
AssertFalse 布尔,为False
AssertTrue 布尔,为True
DecimalMax 数字,最大为value
DecimalMin 数字,最小为value
Digits 数字,是否等于某个值
Email 邮箱
Future 是否是未来时间
FutureOrPresent 时间是否是将来或者现在
Max 数字,最大为value
Min 数字,最小为value
Negative 数字,是否是负数
NegativeOrZero 数字,负数或0
NotBlank 字符串,不为空字符串
NotEmpty 集合,不为空,对于List类型或者是数组类型
NotNull 对象,不为空
Null 对象,为空
Past 日期,过去的日期
PastOrPresent 过去或者现在
Pattern 字符串,正则校验
Positive 数字,正数
PositiveOrZero 数字,正数或0
Size min<=value<=max

源码

  • Github:https://github.com/lengrongfu/validate-common-demo

LRF 记录学习、生活的点滴