美文网首页自动化测试Springboot
springboot 参数校验详解

springboot 参数校验详解

作者: 土豆肉丝盖浇饭 | 来源:发表于2018-01-03 18:18 被阅读82次

    在日常开发写rest接口时,接口参数校验这一部分是必须的,但是如果全部用代码去做,显得十分麻烦,spring也提供了这部分功能,本文来探究一下如何实现

    1.配置

    spring-boot-starter-web包自动依赖hibernate-validator,不用再重复引入,直接开搞

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.3.1.Final</version>
    </dependency>
    

    声明一个bean注册到spring容器,这个bean是一个容器后处理器,会把校验的逻辑通过AOP织入有@Validated注解的class,具体可以看这个类的源码

    这一步在springboot其实也不用做,ValidationAutoConfiguration这个配置类自动帮我们做了

     @Bean
        public MethodValidationPostProcessor methodValidationPostProcessor(){
         return new MethodValidationPostProcessor();
        }
    

    验证不通过会产生异常,因为我们项目提供rest接口,所以通过全局捕获异常,然后转换为json给前台

    
    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        /**
         * 用来处理bean validation异常
         * @param ex
         * @return
         */
        @ExceptionHandler(ConstraintViolationException.class)
        @ResponseBody
        public  WebResult resolveConstraintViolationException(ConstraintViolationException ex){
            WebResult errorWebResult = new WebResult(WebResult.FAILED);
            Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
            if(!CollectionUtils.isEmpty(constraintViolations)){
                StringBuilder msgBuilder = new StringBuilder();
                for(ConstraintViolation constraintViolation :constraintViolations){
                    msgBuilder.append(constraintViolation.getMessage()).append(",");
                }
                String errorMessage = msgBuilder.toString();
                if(errorMessage.length()>1){
                    errorMessage = errorMessage.substring(0,errorMessage.length()-1);
                }
                errorWebResult.setInfo(errorMessage);
                return errorWebResult;
            }
            errorWebResult.setInfo(ex.getMessage());
            return errorWebResult;
        }
    
        @ExceptionHandler(MethodArgumentNotValidException.class)
        @ResponseBody
        public WebResult resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex){
            WebResult errorWebResult = new WebResult(WebResult.FAILED);
            List<ObjectError>  objectErrors = ex.getBindingResult().getAllErrors();
            if(!CollectionUtils.isEmpty(objectErrors)) {
                StringBuilder msgBuilder = new StringBuilder();
                for (ObjectError objectError : objectErrors) {
                    msgBuilder.append(objectError.getDefaultMessage()).append(",");
                }
                String errorMessage = msgBuilder.toString();
                if (errorMessage.length() > 1) {
                    errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
                }
                errorWebResult.setInfo(errorMessage);
                return errorWebResult;
            }
            errorWebResult.setInfo(ex.getMessage());
            return errorWebResult;
        }
    }
    

    这两个异常分别对应校验的两种使用方式

    1. 在方法里面校验
    2. 在bean对象里面校验
      经过测试,以上两种形式的数据验证不仅仅对controller层有用,在service层也行,只要这个类在spring ioc容器里面

    2.使用

    2.1常用校验注解

    @AssertFalse 校验false
    @AssertTrue 校验true
    @DecimalMax(value=,inclusive=) 小于等于value,
    inclusive=true,是小于等于
    @DecimalMin(value=,inclusive=) 与上类似
    @Max(value=) 小于等于value
    @Min(value=) 大于等于value
    @NotNull  检查Null
    @Past  检查日期
    @Pattern(regex=,flag=)  正则
    @Size(min=, max=)  字符串,集合,map限制大小
    @Valid 对po实体类进行校验
    

    这篇文章介绍的注解更全一点

    2.2在方法参数上使用

    @Controller
    @Validated
    public class ValidationController {
    
        @GetMapping("/validate1")
        @ResponseBody
        public String validate1(
                @Size(min = 1,max = 10,message = "姓名长度必须为1到10")@RequestParam("name") String name,
                @Min(value = 10,message = "年龄最小为10")@Max(value = 100,message = "年龄最大为100") @RequestParam("age") Integer age,
                @Future @RequestParam("birth")@DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss") Date birth){
            return "validate1";
        }
    }
    

    注意类名需要加注解@Validated
    校验失败会抛出ConstraintViolationException异常
    然后我们在全局异常捕获类捕获这个异常,返回给前台对应的错误json

    2.3在bean内属性上使用

    给model类增加校验注解

    public class User {
    
        @Size(min = 1,max = 10,message = "姓名长度必须为1到10")
        private String name;
    
        @NotEmpty
        private String firstName;
    
        @Min(value = 10,message = "年龄最小为10")@Max(value = 100,message = "年龄最大为100")
        private Integer age;
    
        @Future
        @JSONField(format="yyyy-MM-dd HH:mm:ss")
        private Date birth;
    
        ...getter setter
    }
    

    在controller对应User实体前增加@Valid注解

    @PostMapping("/validate2")
    @ResponseBody
    public User validate2(@Valid @RequestBody User user){
        return user;
    }
    

    3.扩展

    除了默认提供的校验注解外,我们可以定义自己的校验注解

    3.1.创建约束注解类

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
    @Retention(RUNTIME)
    @Documented
    @Constraint(validatedBy = { HandsomeBoyValidator.class})
    public @interface HandsomeBoy {
    
        String message() default "盛超杰最帅";
    
        String name();
    
        Class<?>[] groups() default { };
    
        Class<? extends Payload>[] payload() default {};
    }
    

    注意:message用于显示错误信息这个字段是必须的,groups和payload也是必须的
    @Constraint(validatedBy = { HandsomeBoyValidator.class})用来指定处理这个注解逻辑的类

    一开始写了这个自定义注解和验证类,发现没有生效,最后发现是@Constraint这个注解里的类没有配置,还跟了很多源码,蛋疼,总的来讲,这个配置还是挺方便的

    外国人写的一篇博客,介绍自定义验证配置,挺全的

    3.2.创建验证器类

    public class HandsomeBoyValidator implements ConstraintValidator<HandsomeBoy, User> {
    
        private String name;
    
        /**
         * 用于初始化注解上的值到这个validator
         * @param constraintAnnotation
         */
        @Override
        public void initialize(HandsomeBoy constraintAnnotation) {
            name =constraintAnnotation.name();
        }
    
        /**
         * 具体的校验逻辑
         * @param value
         * @param context
         * @return
         */
        @Override
        public boolean isValid(User value, ConstraintValidatorContext context) {
            return name ==null || name.equals(value.getName());
        }
    }
    

    这边的功能是user类里面的name字段必须和配置的一样,否则输出一个事实

    3.3. 测试

    @PostMapping("/validate3")
    @ResponseBody
    public User validate3(@Valid @HandsomeBoy(name = "scj",message = "盛超杰第二帅") @RequestBody  User user){
        return user;
    }
    

    如果验证不通过,会输出盛超杰第二帅,全局异常处理器不要忘记配置

    4.demo源码下载

    https://github.com/shengchaojie/springboot-validation-demo

    相关文章

      网友评论

      本文标题:springboot 参数校验详解

      本文链接:https://www.haomeiwen.com/subject/wskknxtx.html