SpringBoot整合校验框架validation

本文介绍在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>

项目架子:
SpringBoot整合校验框架validation_第1张图片
SpringBoot整合校验框架validation_第2张图片

二:校验规则

2.1 使用原生注解-swagger注解可以忽略

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));
    }  
}

2.3 exception包中做统一异常拦截

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);
    }
}

2.4 自定义校验规则1 — 校验实体中的flag字段(只能在1,2,3中选一个值)

@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;
    }
}

2.5 自定义校验规则2 — 校验正确手机号

@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();
    }
}

三:开始实验校验功能。

  • 启动项目,去swagger页面测试结果(我用的swagger,你们自行决定接口测试方法)。
  • 校验对象:如下,完全OJBK,没问题。
  • 校验单个参数:如下,完全OJBK,没问题。

四:补充常用标签含义

限制说明
@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格式

你可能感兴趣的:(JAVA)