SpringBoot通用异常处理

简介: 通用异常返回一般用在该异常服务器无法处理的时候,进行消息的返回。所以返回代码只有 500。

一、通用异常返回

通用异常返回一般用在该异常服务器无法处理的时候,进行消息的返回。所以返回代码只有 500。


/**
 * 通用异常
 */
@Getter
@Setter
public class CommonException extends RuntimeException {
   

    private final Integer code;

    private final String msg;

    public CommonException() {
   
        super("服务器异常");
        this.code = 500;
        this.msg = "服务器异常";
    }

    public CommonException(String msg, Object... arguments) {
   
        super(CharSequenceUtil.format(msg, arguments));
        this.code = 500;
        this.msg = CharSequenceUtil.format(msg, arguments);
    }

    public CommonException(Integer code, String msg, Object... arguments) {
   
        super(CharSequenceUtil.format(msg, arguments));
        this.code = code;
        this.msg = CharSequenceUtil.format(msg, arguments);
    }
}

二、未知错误返回格式重写

将未知错误异常,输出格式重写为我们熟悉的响应格式。


/**
 * 将未知错误异常,输出格式重写为我们熟悉的响应格式
 **/
@Component
public class GlobalErrorAttributesHandler extends DefaultErrorAttributes {
   

    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions attributeOptions) {
   
         final String T = "服务器异常,请求地址:";
        // 获取spring默认的返回内容
        Map<String, Object> defaultErrorAttributes = super.getErrorAttributes(webRequest, attributeOptions);

        // 获取其状态码
        Object status = defaultErrorAttributes.get("status");
        if (ObjectUtil.isNotEmpty(status)) {
   
            // 如果其为404,则处理
            if (HttpStatus.HTTP_NOT_FOUND == Convert.toInt(status)) {
   
                Object path = defaultErrorAttributes.get("path");
                if(ObjectUtil.isNotEmpty(path)) {
   
                    return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_NOT_FOUND, "路径不存在,请求地址:" +
                            Convert.toStr(path), null));
                } else {
   
                    return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_NOT_FOUND, "路径不存在", null));
                }
            } else {
   
                return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_INTERNAL_ERROR, T +
                        CommonServletUtil.getRequest().getRequestURL(), null));
            }
        }

        // 如果返回的异常是CommonException,则按CommonException响应的内容进行返回
        Throwable throwable = this.getError(webRequest);
        if (ObjectUtil.isNotEmpty(throwable)) {
   
            if (throwable instanceof CommonException) {
   
                CommonException commonException = (CommonException) throwable;
                return BeanUtil.beanToMap(CommonResult.error(commonException.getMsg()));
            } else {
   
                return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_INTERNAL_ERROR, T +
                        CommonServletUtil.getRequest().getRequestURL(), null));
            }
        } else {
   
            // throwable为空,则直接返回默认异常
            return BeanUtil.beanToMap(CommonResult.error(T + CommonServletUtil.getRequest().getRequestURL()));
        }
    }
}

三、各类异常处理

3.1 全局异常页面处理

全局异常页面处理器,覆盖默认的 Whitelabel Error Page

/**
 * 全局异常页面处理器,覆盖默认的 Whitelabel Error Page
 *
 **/
@Slf4j
@RestController
public class GlobalErrorViewController {
   

    /**
     * Error页面视图,直接响应JSON
     **/
    @RequestMapping("/errorView")
    public CommonResult<String> globalError(HttpServletRequest request) {
   
        CommonResult<String> commonResult = new CommonResult<>(404, "路径不存在", null);
        Object model = request.getAttribute("model");
        if(ObjectUtil.isNotEmpty(model) && (model instanceof Exception)){
   
                if(model instanceof CommonException) {
   
                    JSONObject errorObj = JSONUtil.parseObj(model);
                    Integer code = errorObj.getInt("code");
                    String msg = errorObj.getStr("msg");
                    if(ObjectUtil.isAllNotEmpty(code, msg)) {
   
                        commonResult.setCode(code).setMsg(msg);
                    } else if(ObjectUtil.isNotEmpty(msg)) {
   
                        commonResult = CommonResult.error(msg);
                    } else {
   
                        commonResult = CommonResult.error();
                    }
                } else {
   
                    commonResult = CommonResult.error();
                    log.error(">>> 服务器未知异常,具体信息:", (Exception) model);
                }

        }
        return commonResult;
    }
}
@RestController
public class GlobalErrorViewHandler extends BasicErrorController {
   

    public GlobalErrorViewHandler(ServerProperties serverProperties) {
   
        super(new GlobalErrorAttributesHandler(), serverProperties.getError());
    }

    /**
     * 覆盖默认的Json响应
     **/
    @Override
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
   
        Map<String, Object> defaultErrorAttributes = super.getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
        Integer code = Convert.toInt(defaultErrorAttributes.get("code"));
        return new ResponseEntity<>(defaultErrorAttributes, HttpStatus.valueOf(ObjectUtil.isNotEmpty(code)?code:500));
    }

    /**
     * 覆盖默认的错误页面,响应JSON
     */
    @Override
    @RequestMapping(produces = {
   "text/html"})
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
   
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        request.setAttribute("model", model);
        return modelAndView != null ? modelAndView : new ModelAndView("errorView", model);
    }
}

3.2 全局异常处理器

@ControllerAdvice
public class GlobalExceptionHandler {
   

    /**
     * 不同异常返回不同结果
     **/
    @ResponseBody
    @ExceptionHandler
    public CommonResult<String> handleException(Exception e) {
   
        return GlobalExceptionUtil.getCommonResult(e);
    }
}

3.3 全局异常处理工具类

这里我们将可能遇到的异常和对应的处理器写入,像这样:

/**
 * 全局异常处理工具类,将异常转为通用结果
 */
@Slf4j
public class GlobalExceptionUtil {
   


    /**
     * 实用程序类是静态成员的集合,并不意味着可以实例化。 即使是可以扩展的抽象实用程序类也不应该具有公共构造函数。
     * Java 向每个未显式定义至少一个的类添加一个隐式公共构造函数。 因此,至少应该定义一个非公共构造函数。
     */
    private GlobalExceptionUtil(){
   
       // TODO
    }


    /**
     * 根据错误类型获取对应的CommonResult
     **/
    public static CommonResult<String> getCommonResult(Exception e) {
   
      CommonResult<String> commonResult;

      if (e instanceof HttpMessageNotReadableException) {
   
            // 如果是参数传递格式不支持异常 415
            if (Objects.requireNonNull(e.getMessage()).contains("JSON parse error")) {
   
                e.printStackTrace();
                //JSON格式转换错误特殊提示
                commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "参数格式错误", null);
            } else {
   
                commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "请使用JSON方式传参", null);
            }
        }
      else if (e instanceof HttpMediaTypeNotSupportedException) {
   
            e.printStackTrace();
            // 如果是JSON参数格式错误异常 415
            commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "参数格式错误", null);
        }
      else if (e instanceof MethodArgumentNotValidException) {
   
            // 如果是参数校验异常(MethodArgumentNotValidException) 415
            MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException) e;
            commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, getArgNotValidMessage(methodArgumentNotValidException.getBindingResult()), null);
        }
      else if (e instanceof BindException) {
   
            // 如果是参数校验异常(BindException) 415
            BindException bindException = (BindException) e;
            commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, getArgNotValidMessage(bindException.getBindingResult()), null);
        }
      else if (e instanceof ConstraintViolationException) {
   
            // 如果是参数校验异常(ConstraintViolationException) 415
            ConstraintViolationException constraintViolationException = (ConstraintViolationException) e;
            commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, getArgNotValidMessage(constraintViolationException.getConstraintViolations()), null);
        }
      else if (e instanceof MissingServletRequestParameterException) {
   
            // 如果是参数校验异常(MissingServletRequestParameterException) 415
            MissingServletRequestParameterException missingServletRequestParameterException = (MissingServletRequestParameterException) e;
            commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, missingServletRequestParameterException.getMessage(), null);
        }
      else if (e instanceof SaTokenException) {
   
          // 如果是SaToken相关异常,则由AuthExceptionUtil处理
          return AuthExceptionUtil.getCommonResult(e);
      }
      else if (e instanceof MultipartException) {
   
            //文件上传错误特殊提示
            commonResult = CommonResult.error("请使用multipart/form-data方式上传文件");
        }
      else if (e instanceof MissingServletRequestPartException) {
   
            //文件上传错误特殊提示
            commonResult = CommonResult.error("请选择要上传的文件并检查文件参数名称是否正确");
        }
      else if(e instanceof MyBatisSystemException) {
   
            // 如果是MyBatisSystemException
            Throwable cause = e.getCause();
            if (cause instanceof PersistenceException) {
   
                Throwable secondCause = cause.getCause();
                if (secondCause instanceof CommonException) {
   
                    CommonException commonException = (CommonException) secondCause;
                    commonResult = CommonResult.get(commonException.getCode(), commonException.getMsg(), null);
                } else {
   
                    e.printStackTrace();
                    commonResult = CommonResult.error("数据操作异常");
                }
            } else {
   
                e.printStackTrace();
                commonResult = CommonResult.error("数据操作异常");
            }
        }
      else if (e instanceof CommonException) {
   
            // 通用业务异常,直接返回给前端
            CommonException commonException = (CommonException) e;
            commonResult = CommonResult.get(commonException.getCode(), commonException.getMsg(), null);
        }
      else if (e instanceof HttpRequestMethodNotSupportedException){
   
          // 如果是请求方法异常 405
          String method = CommonServletUtil.getRequest().getMethod();
          if (HttpMethod.GET.toString().equals(method)) {
   
              commonResult = CommonResult.get(HttpStatus.HTTP_BAD_METHOD, "请求方法应为POST", null);
          } else if(HttpMethod.POST.toString().equals(method)) {
   
              commonResult = CommonResult.get(HttpStatus.HTTP_BAD_METHOD, "请求方法应为GET", null);
          } else {
   
              commonResult = CommonResult.get(HttpStatus.HTTP_BAD_METHOD, "请求方法仅支持GET或POST", null);
          }
         }
      else {
   
            // 未知异常打印详情
            e.printStackTrace();
            // 未知异常返回服务器异常
            commonResult = CommonResult.error("服务器异常");
        }
        log.error(">>> {},请求地址:{}", commonResult.getMsg(), CommonServletUtil.getRequest().getRequestURL());
        return commonResult;
    }

    /**
     * 获取请求参数不正确的提示信息,多个信息,拼接成用逗号分隔的形式
     **/
    public static String getArgNotValidMessage(Set<ConstraintViolation<?>> constraintViolationSet) {
   
        if (ObjectUtil.isEmpty(constraintViolationSet)) {
   
            return "";
        }
        StringBuilder stringBuilder = StrUtil.builder();

        // 多个错误用逗号分隔
        for (ConstraintViolation<?> constraintViolation : constraintViolationSet) {
   
            stringBuilder.append(StrPool.COMMA).append(constraintViolation.getMessage());
        }

        // 最终把首部的逗号去掉
        return CharSequenceUtil.removePrefix(stringBuilder.toString(), StrPool.COMMA);
    }

    /**
     * 获取请求参数不正确的提示信息,多个信息,拼接成用逗号分隔的形式
     **/
    public static String getArgNotValidMessage(BindingResult bindingResult) {
   
        if (ObjectUtil.isNull(bindingResult)) {
   
            return "";
        }
        StringBuilder stringBuilder = StrUtil.builder();

        // 多个错误用逗号分隔
        List<ObjectError> allErrorInfos = bindingResult.getAllErrors();
        for (ObjectError error : allErrorInfos) {
   
            stringBuilder.append(StrPool.COMMA).append(error.getDefaultMessage());
        }

        // 最终把首部的逗号去掉
        return CharSequenceUtil.removePrefix(stringBuilder.toString(), StrPool.COMMA);
    }
}
目录
相关文章
|
Web App开发 前端开发 Java
SpringBoot默认200个线程对于Websocket长连接够用吗?(一)
上篇推文从源码剖析SpringBoot中Tomcat的默认最大连接数中我们知道,SpringBoot的内嵌Tomcat默认的最大连接数为200。那么,这个默认值对于项目中引入了WebSocket使用长连接后,是否足够用呢?今天强哥就带大家一起从源码的角度来分析一下。
SpringBoot默认200个线程对于Websocket长连接够用吗?(一)
|
安全 前端开发 Java
Spring Security 自定义异常失效?从源码分析到解决方案
Spring Security 自定义异常失效?从源码分析到解决方案
|
10月前
|
存储 监控 NoSQL
【赵渝强老师】MongoDB文档级别的并发控制
MongoDB使用WiredTiger存储引擎在文档级别进行并发控制,允许多个写操作同时修改不同文档,但对同一文档的修改需序列化执行。引擎采用乐观锁和意向锁机制处理冲突。通过视频讲解、插入大量文档示例及使用`mongotop`和`db.serverStatus()`命令,演示了如何监控MongoDB的锁信息和读写统计,展示了数据库和集合级别的写锁情况。
251 29
|
12月前
|
JavaScript 搜索推荐 前端开发
DevDocs具备**一站式搜索、多语言支持、离线访问等**特色功能。
DevDocs具备**一站式搜索、多语言支持、离线访问等**特色功能。
230 56
|
11月前
|
网络协议 应用服务中间件 网络安全
免费泛域名https证书教程—无限免费续签
随着互联网安全意识提升,越来越多网站采用HTTPS协议。本文介绍如何通过JoySSL轻松获取并实现免费泛域名SSL证书的无限续签。JoySSL提供永久免费通配符SSL证书,支持无限制域名申请及自动续签,全中文界面适合国内用户。教程涵盖注册账号、选择证书类型、验证域名所有权、下载与安装证书以及设置自动续签等步骤,帮助网站简化SSL证书管理流程,确保长期安全性。
|
监控 算法 应用服务中间件
“四两拨千斤” —— 1.2MB 数据如何吃掉 10GB 内存
一个特殊请求引发服务器内存用量暴涨进而导致进程 OOM 的惨案。
255 14
Threejs使用CubeCamera实现环境映射
这篇文章详细介绍了如何在Three.js中使用CubeCamera来实现环境映射,包括创建CubeCamera、设置反射材质以及实时更新渲染结果的具体步骤。
299 3
|
算法
计算机网络——数据链路层-差错检测(奇偶校验、循环冗余校验CRC)
计算机网络——数据链路层-差错检测(奇偶校验、循环冗余校验CRC)
1346 0
|
机器学习/深度学习 人工智能 自然语言处理
还不懂如何与AI高效交流?保姆级且全面的chatGPT提示词工程教程来啦!(一)基础篇
这篇文章是一篇保姆级的教程,旨在全面介绍如何与AI进行高效交流,包括ChatGPT的前世今生、应用场景以及提问的基础技巧。
还不懂如何与AI高效交流?保姆级且全面的chatGPT提示词工程教程来啦!(一)基础篇
下一篇
开通oss服务