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);
    }
}
目录
相关文章
|
缓存 前端开发 Java
SpringBoot&SpringMVC统一异常处理之RestControllerAdvice
SpringBoot&SpringMVC统一异常处理之RestControllerAdvice
125 0
|
存储 运维 Java
SpringBoot 统一异常处理(附核心工具类-ErrorInfoBuilder) 1
SpringBoot 统一异常处理(附核心工具类-ErrorInfoBuilder)
|
6月前
|
JSON Java 应用服务中间件
SpringBoot之异常处理
SpringBoot之异常处理
|
Java Spring
springboot 高级教程 全局异常处理
springboot 高级教程 全局异常处理
196 0
|
人工智能 Java
SpringBoot实战(十):统一异常处理
SpringBoot实战(十):统一异常处理
|
JSON 前端开发 Java
SpringBoot 统一异常处理(附核心工具类-ErrorInfoBuilder) 2
SpringBoot 统一异常处理(附核心工具类-ErrorInfoBuilder)
|
JSON Java 应用服务中间件
SpringBoot 学习笔记之 “异常处理”
SpringBoot 学习笔记之 “异常处理”
|
Java
SpringBoot实现统一异常处理
大家在使用SpringBoot开发项目的时候肯定都需要处理异常吧,没有处理异常那么异常信息直接显示给用户这是非常不雅观的,同时还可能造成用户误会,那么今天我们就来简单的写一下如何在SpringBoot项目中实现统一的异常处理。
493 0
|
Java 数据安全/隐私保护
Springboot统一异常处理
Springboot统一异常处理
|
JSON Java 数据格式
对Springboot项目进行统一异常处理
对Springboot项目进行统一异常处理
对Springboot项目进行统一异常处理