• springboot统一异常处理(返回json)并支持所有异常格式化返回


    统一异常处理

    当用spring boot开发后端时,我们常采用前后端分离的开发策略,这时候要求前端与后端需要进行数据交互,传统的一般采用json数据交互。

    这时候我们要对spring boot的默认异常处理方式进行修改了,要统一返回数据格式,优雅的数据交互,优雅的开发应用。

    首先我们要了解一般springboot的错误发生在什么地方。

    一般发生在controller业务层filter中(如spring security)拦截器中、还有就是未知错误了。

    下面我分享一下我的处理方式: @RestControllerAdvice+filter+重写BasicErrorController的处理方式。

    @RestControllerAdvice

    @RestControllerAdvice+@ExceptionHandler(Exception.class)可以处理经过controller的异常错误

    @RestControllerAdvice
    public class BaseExceptionHandler {
        private static final Logger logger = LoggerFactory.getLogger(BaseExceptionHandler.class);
    
        /**
         * 未知异常
         * @param e 异常
         * @return 统一结果返回
         */
        @ExceptionHandler(Exception.class)
        public R exception(Exception e) {
            logger.error(e.getMessage(), e);
            return R.error().message(e.getMessage());
        }
        @ExceptionHandler(NoHandlerFoundException.class)
        public R noHandlerFoundException(){
            return R.error().message("当前页面不存在");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    filter捕获所有异常

    当错误没有经过controller时,@RestControllerAdvice是不能捕获的,这时候就需要在filter进行处理。

    public class ExceptionHandlerFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain){
            try {
                filterChain.doFilter(servletRequest, servletResponse);
            } catch (Exception e) {
                ResponseUtils.out((HttpServletResponse) servletResponse, R.error().message(e.getMessage()));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    记得filter一定要注册到springboot才有作用,下面是我喜欢的一种方式

    @Bean
    FilterRegistrationBean<ExceptionHandlerFilter> exceptionFilterRegistrationBean() {
        FilterRegistrationBean<ExceptionHandlerFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new ExceptionHandlerFilter());
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);//放在最前面
        return bean;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    未知异常处理

    当发生的异常不知道在哪时,springboot会进行默认异常处理,跳转到 /error

    这时我们要统一数据返回就必须要从这下手了,创建一个新的Controller处理/error,继承BasicErrorController,并覆盖其中的方法

    @RestController
    public class MyErrorController extends BasicErrorController {
    
        @Autowired
        public MyErrorController(ErrorAttributes errorAttributes,
                                 ServerProperties serverProperties,
                                 List<ErrorViewResolver> errorViewResolvers) {
            super(errorAttributes, serverProperties.getError(), errorViewResolvers);
        }
    
    
        @Override
        public ModelAndView errorHtml(HttpServletRequest request,
                                      HttpServletResponse response) {
            HttpStatus status = getStatus(request);
            Map<String, Object> model = Collections
                    .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
    //        return new ModelAndView("error", model, status);
            ResponseUtils.out(response, R.error().code(status.value()).message((String) model.get("error")));
            return null;
        }
    
        @Override
        public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
            Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
    
            //可以换成项目中自定义的通信json
            Map<String, Object> resultBody = new HashMap<>(16);
            resultBody.put("success", false);
            resultBody.put("code", body.get("status"));
            resultBody.put("message", body.get("error"));
            Map<String, Object> data = new HashMap<>();
            data.put("timestamp", body.get("timestamp"));
            data.put("path", body.get("path"));
            resultBody.put("data", data);
            return new ResponseEntity<>(resultBody, HttpStatus.OK);
        }
    }
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    配置文件

    spring:
      mvc:
        throw-exception-if-no-handler-found: true
      web:
        resources:
          add-mappings: false
    server:
      error:
        include-exception: true
      servlet:
        encoding:
          charset: utf-8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    统一结果返回的工具类

    ResponseUtils

    public class ResponseUtils {
        public static void out(HttpServletResponse response, R r) {
            ObjectMapper mapper = new ObjectMapper();
            response.setStatus(HttpStatus.OK.value());
            response.setContentType("application/json;charset=UTF-8");
            try {
                mapper.writeValue(response.getWriter(), r);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    R

    @Data
    public class R implements Serializable {
        private boolean success;
    
        private Integer code;
        private String message;
        private Map data;
        private R(){}
        public static R ok(){
            R r = new R();
            r.setSuccess(true);
            r.setCode(200);
            r.setMessage("成功");
            r.setData(new HashMap<>());
            return r;
        }
        public static R error() {
            R r = new R();
            r.setSuccess(false);
            r.setCode(500);
            r.setMessage("失败");
            r.setData(new HashMap<>());
            return r;
        }
    
    
        public R success(Boolean success){
            this.success = success;
            return this;
        }
        public R code(Integer code){
            this.code = code;
            return this;
        }
        public R message(String message){
            this.message = message;
            return this;
        }
    
        public R data(String key, Object value){
            this.data.put(key,value);
            return this;
        }
        public R data(Map map){
            this.setData(map);
            return this;
        }
    
    }
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
  • 相关阅读:
    2022年10月21日数据库实验内容
    第3关:集合操作100
    eclipse 根据wsdl文件生成Java文件 3种方式
    LVS-DR
    开源大赛的种种收获
    28. 找出字符串中第一个匹配项的下标 KMP C++
    kubeadm安装配置指南
    影响多用户商城系统价格的因素有哪些?
    光环云出席Enjoy出海AIGC主题研讨会,助力企业迎接AI时代机遇与挑战
    EDP .Net开发框架--权限
  • 原文地址:https://blog.csdn.net/zhongjianboy/article/details/126322783