zoukankan      html  css  js  c++  java
  • springboot中@Valid注解与@Validated注解区别以及全局异常的处理

    前端传过来数据的时候,要进行校验,但是大量的校验很繁琐,会造成大量的if else语句的产生,所以@Valid和@Validated很好的解决了这个问题.

    首先说一下两个注解的区别:

      1.两者的所属的包是不同的

        @Valid属于javax.validation包下,是jdk给提供的

        @Validated是org.springframework.validation.annotation包下的,是spring提供的

      2.@Validated要比@Valid更加强大

        @Validated在@Valid之上提供了分组功能和验证排序功能

    一.处理校验的异常

    首先定义一个实体类:

    @Data
    public class Person {
        @NotEmpty(message = "姓名不能为空")
        private String name;
        @Max(value = 18,message = "年龄不能超过18岁")
        private String age;
        @Max(value = 1, message = "性别只能为0和1: 0=女1=男")
        @Min(value = 0, message = "性别只能为0和1: 0=女1=男")
        private Short sex;
    }

    然后controller,BindingResult对象,用于获取校验失败情况下的反馈信息:

    @RestController
    @Slf4j
    public class VerifyController {
    
        @PostMapping(value = "/valid")
        public void verifyValid(@Valid @RequestBody Person person, BindingResult result) {
            log.info("I am verifyValid() method, the request params is: 【{}】", JSON.toJSONString(person));
            if (result.hasErrors()) {
                FieldError fieldError = result.getFieldError();
                if (fieldError != null) {
                    log.error("error msg: 【{}】", fieldError.getDefaultMessage());
                }
            }
        }
    
        @PostMapping(value = "/validated")
        public void verifyValidated(@Validated @RequestBody Person person, BindingResult result) {
            log.info("I am verifyValidated() method, the request params is: 【{}】", JSON.toJSONString(person));
            if (result.hasErrors()) {
                FieldError fieldError = result.getFieldError();
                if (fieldError != null) {
                    log.error("error msg: 【{}】", fieldError.getDefaultMessage());
                }
            }
        }
    }

    此时访问两个controller结果都可以进行校验:

    /validated:

    2020-01-14 12:32:54.805  INFO 25244 --- [nio-8080-exec-1] com.example.controller.VerifyController  : I am verifyValidated() method, the request params is: 【{"age":"19","name":"","sex":10}】
    2020-01-14 12:32:54.806 ERROR 25244 --- [nio-8080-exec-1] com.example.controller.VerifyController  : error msg: 【性别只能为0和1: 0=女1=男】
    2020-01-14 12:33:10.638  INFO 25244 --- [nio-8080-exec-2] com.example.controller.VerifyController  : I am verifyValidated() method, the request params is: 【{"age":"19","name":"","sex":1}】
    2020-01-14 12:33:10.639 ERROR 25244 --- [nio-8080-exec-2] com.example.controller.VerifyController  : error msg: 【年龄不能超过18岁】
    2020-01-14 12:33:18.034  INFO 25244 --- [nio-8080-exec-3] com.example.controller.VerifyController  : I am verifyValidated() method, the request params is: 【{"age":"18","name":"","sex":1}】
    2020-01-14 12:33:18.034 ERROR 25244 --- [nio-8080-exec-3] com.example.controller.VerifyController  : error msg: 【姓名不能为空】

    /valid:

    2020-01-14 12:35:19.151  INFO 25244 --- [nio-8080-exec-5] com.example.controller.VerifyController  : I am verifyValid() method, the request params is: 【{"age":"19","name":"","sex":10}】
    2020-01-14 12:35:19.151 ERROR 25244 --- [nio-8080-exec-5] com.example.controller.VerifyController  : error msg: 【姓名不能为空】
    2020-01-14 12:35:24.306  INFO 25244 --- [nio-8080-exec-6] com.example.controller.VerifyController  : I am verifyValid() method, the request params is: 【{"age":"19","name":"aa","sex":10}】
    2020-01-14 12:35:24.306 ERROR 25244 --- [nio-8080-exec-6] com.example.controller.VerifyController  : error msg: 【年龄不能超过18岁】
    2020-01-14 12:35:29.565  INFO 25244 --- [nio-8080-exec-7] com.example.controller.VerifyController  : I am verifyValid() method, the request params is: 【{"age":"18","name":"aa","sex":10}】
    2020-01-14 12:35:29.565 ERROR 25244 --- [nio-8080-exec-7] com.example.controller.VerifyController  : error msg: 【性别只能为0和1: 0=女1=男】

    在有些时候我们不一定能够使用BindingResult result来处理校验的结果集,在实际的生产环境中,更方便的是获取该异常然后进行返回.

    通过观察发现,两者在使用@RequestBody参数注解的情况下,两者抛出的都是MethodArgumentNotValidException异常:

    org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public void com.example.controller.VerifyController.verifyValidated(com.example.model.Person) with 3 errors: [Field error in object 'person' on field 'age': rejected value [19]; codes [Max.person.age,Max.age,Max.java.lang.String,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.age,age]; arguments []; default message [age],18]; default message [年龄不能超过18岁]] [Field error in object 'person' on field 'name': rejected value []; codes [NotEmpty.person.name,NotEmpty.name,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.name,name]; arguments []; default message [name]]; default message [姓名不能为空]] [Field error in object 'person' on field 'sex': rejected value [10]; codes [Max.person.sex,Max.sex,Max.java.lang.Short,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.sex,sex]; arguments []; default message [sex],1]; default message [性别只能为0和1: 0=女1=男]] 
        at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:139)
        at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
        at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)

    为了统一抓取异常,首先定义一个全局异常类:

    @ControllerAdvice
    @ResponseBody
    public class GlobleExceptionHandler {
        /**
         * 要拦截的异常Exception
         */
        @ExceptionHandler(value = Exception.class)
        public Result<String> exceptionHandler(Exception e) {
            
            if (e instanceof BindException) {
                e.printStackTrace();  //将异常打印出来
                BindException ex = (BindException) e;
                List<ObjectError> allErrors = ex.getAllErrors();
                ObjectError objectError = allErrors.get(0);
                String ms = objectError.getDefaultMessage();
                return Result.error(CodeMsg.BIND_ERROR.fillArgs(ms));
            } else {
                e.printStackTrace();
                return Result.error(CodeMsg.SERVER_ERROR);
            }
        }
    }

    返回值的Result是统一封装了返回给前端的json数据,具体的实现可以在我上一篇博客中查看:

    https://www.cnblogs.com/zhiweiXiaomu/p/12190353.html

    但是如果不在controller接口中加@RequestBody注解,两者抛出的则是BindException:

     

    org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 3 errors

     

    这里提醒一下各位小伙伴,在controller接口中加不加@ResquestBody注解,抛出的校验异常是不一样的.各位在抓取异常的时候需要注意一下.

    二.@Valid和@Validated两者的区别

      两者的主要区别就是关于分组和分组排序了.@Validated中可以进行分组排序

      首先定义两个接口:  

      

    public interface First {
    }
    
    public interface Second {
    }

      这俩接口是用来分组的.

      实体类如下:

    @Data
    public class Person {
        @NotEmpty(groups = First.class, message = "姓名不能为空")
        private String name;
        @Max(value = 18, groups = Second.class,message = "年龄不能超过18岁")
        private String age;
        @Max(value = 1, message = "性别只能为0和1: 0=女1=男")
        @Min(value = 0, message = "性别只能为0和1: 0=女1=男")
        private Short sex;
    }

      然后controller(与上面一样,两个都没有添加分组),多次校验下我们会发现不管是 @Valid 注解还是 @Validated 注解都只会校验没有添加 groups 属性的实体类字段 (此处只校验了 sex 字段)

      也就是说,在实体类中,添加分组的两个字段,name,和age在校验中失效了.

      因为@Valid注解中没有关于分组的参数,所以在@Validated中加入注解,变成下面这样:

    @RestController
    @Slf4j
    public class VerifyController {
    
        @PostMapping(value = "/valid")
        public void verifyValid(@Valid @RequestBody Person person, BindingResult result) {
            log.info("I am verifyValid() method, the request params is: 【{}】", JSON.toJSONString(person));
            if (result.hasErrors()) {
                FieldError fieldError = result.getFieldError();
                if (fieldError != null) {
                    log.error("error msg: 【{}】", fieldError.getDefaultMessage());
                }
            }
        }
        @PostMapping(value = "/validated")
        public void verifyValidated(@Validated(value = First.class) @RequestBody Person person, BindingResult result) {
            log.info("I am verifyValidated() method, the request params is: 【{}】", JSON.toJSONString(person));
            if (result.hasErrors()) {
                FieldError fieldError = result.getFieldError();
                if (fieldError != null) {
                    log.error("error msg: 【{}】", fieldError.getDefaultMessage());
                }
            }
        }
    }

      此时,@Validated则可以校验对应有First分组的name字段了,但是别的字段,也就是说除了加了First分组的name字段,别的字段都不能进行校验.

             那么怎么办呢,因为value是Clazz[]数组格式的,所以可以这样:

      value={First.class,Second.class}

      也可以进行分组排序:

      定义一个分组排序接口:

      

    @GroupSequence({First.class,Second.class})
    public interface Group {
    }

       然后controller中    value=Group.class,就可以对所有已经分组的字段进行校验了,但是没有进行分组的字段是不会被校验的.

      

      

  • 相关阅读:
    在java中有关于反射的皮毛----自己的简略认知
    在java中异常中的题目---重要的一点
    在一个陌生的环境里学习新的-----单例
    在java开发环境中,快捷键的使用及用法
    指针(一)
    #ifdef、#ifndef、#else、#endif执行条件编译
    oc中的数组
    控制循环结构
    oc中的枚举
    oc中类的实例化及方法调用
  • 原文地址:https://www.cnblogs.com/zhiweiXiaomu/p/12191497.html
Copyright © 2011-2022 走看看