SpringBoot通用异常处理

一、通用异常返回

通用异常返回一般用在该异常服务器无法处理的时候,进行消息的返回。所以返回代码只有 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);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timi先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值