• SpringBoot统一返回值和统一异常处理


    在项目开发中,我们通常会约定一个接口返回的数据格式,其中包含codemessagedata等信息,像下面这样:

    {
      "code": 0,
      "message": "success",
      "data": ""
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    没有异常的情况,正常返回数据

    接口数据对应的实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class ResultVo implements Serializable {
        
        private Integer code ;
        private String message ;
        private Object data ;
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    用于生成Result实体的工具类

    public class ResultUtils {
        
        /**
        * 成功时返回的方法(data不为空)
        */
        public static ResultVo success(Object o) {
            return new ResultVo(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage(), o);
        }
        /**
        * 成功时返回的方法(data为空)
        */
        public static ResultVo success() {
            return success(null);
        }
        /**
        * 失败时返回的方法
        */
        public static ResultVo error(Integer code, String message) {
            return new ResultVo(code, message,null);
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    封装code和message的枚举类

    可以约定更多的code和对应的message封装成枚举类,以便不同情况下灵活处理

    public enum ResultEnum {
        
        UNKUOW_ERROR(-1,"未知错误"),
        SUCCESS(0,"success"),
        ERROR(1,"error");
        
        private int code;
        private String message;
        
        ResultEnum(int code ,String message) {
            this.code = code;
            this.message = message;
        }
        //………省略getter and setter
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在Controller中返回

    在Controller中就可以调用ResultUtils的方法进行返回了

    @RequestMapping("/test")
    public ResultVo test(){
        User user = new User();
        user.setName("lisi");
        user.setId(1);
        return ResultUtils.success(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    但是这样在每个请求方法代码中都要对返回数据进行处理,请求返回的返回值也都是ResultVo,不能方便的看到每个方法的返回值。我们使用AOP切面对Controller中的方法进行封装来解决这个问题

    统一返回值切面

    @ControllerAdvice
    public class MyResponseAdvice implements ResponseBodyAdvice<Object> {
    
        @Autowired
        ObjectMapper objectMapper;
    
        //判断是否要执行beforeBodyWrite方法,true为执行,false不执行. 通过该方法可以选择哪些类或那些方法的response要进行处理, 其他的不进行处理.
        @Override
        public boolean supports(MethodParameter returnType, Class converterType) {
            return true;
        }
    
        //对response方法进行具体操作处理
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
            //返回类型是否已经封装
            if (body instanceof ResultVo){
                return body;
            }
            // String特殊处理
            if (body instanceof String) {
                try {
                    return objectMapper.writeValueAsString(ResultUtils.success(body));
                } catch (JsonProcessingException e) {
                    return ResultUtils.error(500,"失败");
                }
            }
            return ResultUtils.success(body);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    有异常的情况,统一异常处理

    自定义异常类

    自定义异常类,继承RuntimeException。为方便使用可以使用枚举类列举出常见的异常情况,这里直接使用了上面的枚举类。

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class BusinessException extends RuntimeException{
    
        private int code;
    
        private String msg;
    
        public BusinessException(ResultEnum resultEnum) {
            this.code = resultEnum.getCode();
            this.msg = resultEnum.getMessage();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    自定统一异常处理器

    @ControllerAdvice
    public class BusinessExceptionHandler {
    
        @ResponseBody
        @ExceptionHandler({BusinessException.class})
        public ResultVo exceptionHandler(BusinessException exception){
            // 省略记录日志
            return new ResultVo(exception.getCode(), exception.getMsg(), null);
        }
    
        @ResponseBody
        @ExceptionHandler({Exception.class})
        public ResultVo exceptionHandler(Exception exception){
            // 省略记录日志
            return new ResultVo(ResultEnum.UNKUOW_ERROR.getCode(), ResultEnum.UNKUOW_ERROR.getMessage(), exception.getMessage());
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在Service层直接抛出异常

    @Override
    public User test(String str) {
    
        if (Objects.isNull(str)){
            throw new BusinessException(ResultEnum.ERROR);
        }
    
        return new User();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    @RequestMapping("/test")
    public User test(){
        User user = userService.test(null);
        return user;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    img

  • 相关阅读:
    Python 反爬虫与反反爬虫
    config_db机制详解
    linux驱动下半部之tasklet
    python文件操作、文件操作、读写文件、写模式
    酒店品牌纷纷冲击中高端,东呈集团能否“快人一步”?
    C语言学习推荐---小游戏
    传统分拣弊端明显,AI机器视觉赋能物流行业包裹分类产线数智化升级
    EDU实战-SQL注入漏洞
    java synchronized
    分离变数法
  • 原文地址:https://blog.csdn.net/qq_43460095/article/details/126055030