本文介绍在Spring Boot中实现对请求的数据进行校验。数据校验常用到概念:
• JSR303/JSR-349: JSR303是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下。JSR-349是其的升级版本,添加了一些新特性。
• hibernate validation:hibernate validation是对这个规范的实现,并增加了一些其他校验注解,如@Email,@Length,@Range等等
• spring validation:spring validation对hibernate validation进行了二次封装,在springmvc模块中添加了自动校验,并将校验信息封装进了特定的类中
本文主要包括如下内容:
• 演示 spring boot validation 校验功能 + 自定义校验注解 + 统一异常处理 。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
entity中实体代码
其中 @FlagValidator(values = “1,2,3”) 和 @PhoneValidator 是自定义校验注解,自定义过程后面说。
其余注解都是自带的,关于自带注解有哪些,见文末的常用标签含义。
@Data
public class UserEntity implements Serializable {
@NotEmpty(message = "姓名不能为空")
private String name;
@Min(value = 18 ,message = "未满18周岁请自觉离开。。。")
@Max(value = 60,message = "您老身体可还吃得消?大于60岁自觉离开。")
private int age;
@Min(value = 666,message = "钱包够憋,不够¥666请自觉充值。")
private double money;
@FlagValidator(values = "1,2,3")
private String flag;
@Email(message = "请输入正确格式的邮箱。。")
@NotEmpty(message = "邮箱不能为空哦!")
private String email;
@PhoneValidator
private String phone;
}
校验对象:在Controller 中请求参数上添加@Validated ,在对应的实体字段上加校验注解即可。
@Api("UserController层")
@RequestMapping(value = "/UserController")
@RestController
public class UserController {
@ApiOperation(value = "向user表插入数据")
@PostMapping(value = {"/insetUser/"})
public ResultMsg<UserEntity> insertUser(@Validated @RequestBody UserEntity userEntity) {
return new ResultMsg<UserEntity>("0000", "success", userEntity);
}
}
校验单个参数
在单个参数前加上校验用注解(比如:@Email),然后再controller类上加注解@Validated
@Api("UserController层")
@RequestMapping(value = "/UserController")
@Validated
@RestController
public class UserController {
@ApiOperation(value = "根据年龄查询")
@GetMapping(value = "/selectByName/{age}")
public ResultMsg<UserEntity> selectByAge(@Min(value = 18,message = "未满18周岁请自觉离开。。") @ApiParam(value = "年龄", required = true) @PathVariable int age) {
UserEntity userEntity = new UserEntity();
userEntity.setAge(age);
return new ResultMsg<UserEntity>("0000", "success", userEntity);
}
@ApiOperation(value = "根据邮箱查询")
@GetMapping(value = "/selectByEmail/{email}")
public ResultMsg<UserEntity> selectByEmail(@Email(message = "请输入正确格式的邮箱。。") @ApiParam(value = "邮箱", required = true) @PathVariable String email) {
UserEntity userEntity = new UserEntity();
userEntity.setEmail(email);
return new ResultMsg<UserEntity>("0000", "success", userEntity);
}
}
校验实体集合,在controller类上加注解@Validated,然后在对应的实体字段上加校验注解即可。
@Api("UserController层")
@RequestMapping(value = "/UserController")
@Validated
@RestController
public class UserController {
@ApiOperation(value = "更新")
@PostMapping("/capacity/batchUpdate")
public BaseResult update(@RequestBody List<CapacityAddUpdateDTO> capacityAddUpdateDTO) {
return new BaseResult<>(capacityService.batchUpdate(capacityAddUpdateDTO));
}
}
controller不做异常处理,此处统一做异常拦截
本文忽略其他异常,只考虑Validate的异常
/**
* validation异常捕获处理 (可能是MethodArgumentNotValidException 或 BindException)
* 2019年12月18日
*/
@RestControllerAdvice
public class ExceptionAop {
/**
* validation 捕获 MethodArgumentNotValidException 异常
* @param e
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResultMsg serviceHandle(MethodArgumentNotValidException e) {
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
StringBuilder sb = new StringBuilder();
allErrors.forEach(err -> sb.append(err.getDefaultMessage()).append(";"));
return new ResultMsg("1111","failed",sb.toString());
}
/**
* validation 捕获 BindException 异常
* @param ex
* @return
*/
@ExceptionHandler(BindException.class)
public ResultMsg handleBindException(BindException ex) {
List<ObjectError> allErrors = ex.getAllErrors();
StringBuilder sb = new StringBuilder();
allErrors.forEach(err -> sb.append(err.getDefaultMessage()).append(";"));
return new ResultMsg("1111","failed",sb.toString());
}
/**
* validation拦截 ConstraintViolationException 异常
* 用于单个异常校验
* @param ex
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
public ResultMsg handleBindException(ConstraintViolationException ex) {
String message = ex.getMessage();
return new ResultMsg("1111","failed",message);
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
// 指定真正实现校验规则的类
@Constraint(validatedBy = FlagValidatorClass.class)
public @interface FlagValidator {
//flag的有效值多个使用','隔开
String values();
// 校验失败,提示内容
String message() default "flag不存在,只能在1,2,3中选一个值";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class FlagValidatorClass implements ConstraintValidator<FlagValidator, Object> {
//临时变量保存flag值列表
private String values;
//初始化values的值
@Override
public void initialize(FlagValidator flagValidator) {
this.values = flagValidator.values();
}
//实现验证
@Override
public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext)
{
String[] value_array = values.split(",");
boolean isFlag = false;
for (int i =0;i<value_array.length;i++)
{
if(value_array[i].equals(value)){
isFlag = true;
break;
}
}
return isFlag;
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
// 指定真正实现校验规则的类
@Constraint(validatedBy = PhoneValidatorClass.class)
public @interface PhoneValidator {
String message() default "不是正确的手机号!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneValidatorClass implements ConstraintValidator<PhoneValidator, String> {
@Override
public void initialize(PhoneValidator phoneValidaton) {}
private static final Pattern PHONE_PATTERN = Pattern.compile(
"^((13[0-9])|(15[^4])|(18[0,2,3,5-9])|(17[0-8])|(147))\\d{8}$"
);
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null || value.length() == 0) {
return true;
}
Matcher m = PHONE_PATTERN.matcher(value);
return m.matches();
}
}
限制说明
@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之间
@Past
验证注解的元素值(日期类型)比当前时间早
@NotEmpty
验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank
验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于 @NotEmpty@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email
验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
@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之间
@Past
验证注解的元素值(日期类型)比当前时间早
@NotEmpty
验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank
验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于 @NotEmpty@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email
验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式