SpringBoot学习笔记之实现全局异常处理


前言

在项目开发中会处理各种类型的异常,那么实现全局统一异常处理是十分必要的。Spring Boot 提供了一个默认的映射:/error,当抛出异常之后,会转到该请求处理。在实际应用中,并不是很符合项目要求。网上 SpringBoot 实现全局异常处理的资料有很多,但也不能拿来就用,需要结合自身项目做些改动。这里就记录一下结合网上众多资料实现自己项目的全局异常处理实践过程。


一、SpringBoot异常处理

1.官方说明

SpringBoot 2.2.6 官网文档

默认情况下,Spring Boot 提供了一个 /error 映射,它以一种合理的方式处理所有错误,并且它在 servlet 容器中注册为一个“全局”错误页面。对于机器客户端,它生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。对于浏览器客户端,有一个 “whitelabel” 错误视图,它以 HTML 格式呈现相同的数据(要自定义它,需要添加一个解析为错误的视图)。要完全替换默认行为,可以实现 ErrorController 并注册该类型的 bean 定义或者添加 ErrorAttributes 类型的 bean 以使用现有机制,但要替换内容。
还可以定义一个带有 @ControllerAdvice 注解的类,以自定义 JSON 文档以返回特定的控制器和/或异常类型。

默认下,SpringBoot 提供了一个 /error 映射,当我们系统发生异常,直接转到 /error 。原始错误页面很简陋,用户体验很不友好。如果想要显示给定状态码的自定义 HTML 错误页,可以将文件添加到 error 文件夹。错误页面可以是静态 HTML (即添加到任何静态资源文件夹下),也可以使用模板构建。
如果替换默认实现异常处理给了两种方式:

  • 实现 ErrorContoller
  • @ControllerAdvice@ExceptionHandler 注解组合使用

官网给出了 @ControllerAdvice@ExceptionHandler 注解实现示例:

@ControllerAdvice(basePackageClasses = AcmeController.class)
public class AcmeControllerAdvice extends ResponseEntityExceptionHandler {

    @ExceptionHandler(YourException.class)
    @ResponseBody
    ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }

}

2.@ControllerAdvice注解和@ExceptionHandler注解

这里我使用 @ControllerAdvice 注解实现全局异常处理。使用注解看着代码简洁。

@ControllerAdvice:这是一个增强的 Controller。使用这个 Controller ,可以实现三个方面的功能:

  • 全局异常处理
  • 全局数据绑定
  • 全局数据预处理

灵活使用这三个功能,可以帮助我们简化很多工作,需要注意的是,这是SpringMVC 提供的功能,在 SpringBoot 中可以直接使用。

@ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度。

二、实现全局异常处理

1.环境说明

开发工具:IDEA 2019.3.1
框架版本:SpringBoot 2.2.6
前端组件库:BootStrap 4.4.1

2.具体实现

由于项目中使用 jQuery Address 实现全站 AJAX 请求,所以全局异常处理只考虑 Ajax 请求,普通页面请求不做实现。

GlobalExceptionHandler - 全局异常统一处理类
代码很少,主要实现全局异常处理和自定义异常处理,统一返回结果集,都封装在 ReturnMsg 类中。

@ControllerAdvice
public class GlobalExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 全局异常处理
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ReturnMsg handleException(HttpServletRequest request, Exception e) {
        // 请求状态
        HttpStatus status = getStatus(request);

        // 返回错误信息:失败代码、失败信息、具体代码、具体信息
        return new ReturnMsg(MsgUtil.MSG_ERROR, "发生异常", String.valueOf(status.value()), status.getReasonPhrase());
    }

    /**
     * 自定义异常处理
     *
     * @param myException
     * @return
     */
    @ExceptionHandler(value = MyException.class)
    @ResponseBody
    public ReturnMsg myErrorHandler(MyException myException) {
        // 返回错误信息:失败代码、失败信息、具体代码、具体信息
        return new ReturnMsg(MsgUtil.MSG_ERROR, "发生异常", String.valueOf(myException.getStatus()), myException.getReason());
    }

    /**
     * 获取请求状态
     *
     * @param request
     * @return
     */
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }

}

ReturnMsg - 响应结果类

public class ReturnMsg {

   /**
    * 结果代码
    */
   private String code;
   
   /**
    * 结果信息
    */
   private String msg;
   
   /**
    * 信息代码
    */
   private String subCode;
   
   /**
    * 信息说明
    */
   private String subMsg;
   
   /**
    * 返回对象
    */
   private Object data;
   
   // 略......
   
   }

MyException - 自定义异常类
继承了 Exception 类,其定义属性参考 SpringBoot 默认异常处理时发送的 Json 数据项。

public class MyException extends Exception{

    /**
     * 异常状态码
     */
    private String status;

    /**
     * 异常原因
     */
    private String reason;

    /**
     * 异常信息
     */
    private String error;

    /**
     * 异常路径
     */
    private String path;
    
    // 略......
    
    }

前台页面接收处理异常
全站 Ajax 请求,所以可以通过设置全局 Ajax 来对异常进行前端处理。

/**
 * 设置全局 AJAX 默认选项
 */
$.ajaxSetup({
   contentType:"application/x-www-form-urlencoded;charset=utf-8",
   complete:function(XMLHttpRequest, status){

      // 全局Ajax访问,处理Ajax清求异常
      var res = XMLHttpRequest.responseText;
      if (typeof JSON.parse(res) == "object") {
         var jsonData = JSON.parse(res);
         // 判断字段是否存在
         if(jsonData.hasOwnProperty("code") && jsonData.code == "ERROR") {

            // 异常信息模态框显示
            $('#exceptionMsgModal h1#subCode').text(jsonData.subCode);
            $('#exceptionMsgModal h3#subMsg').text(jsonData.subMsg);
            $('#exceptionMsgModal').modal('show');

         }
      }
   }
});

3.测试效果

全局异常
这里我们手动制造异常。

/**
 * 系统用户 列表页
 *
 * @param model
 * @return
 */
@WebLog(channel = "web", name = "系统用户列表", action = "/sysUser", saveFlag = true)
@GetMapping("")
public String list(SysUserCriteria sysUserCriteria, Model model) throws MyException {

   // 异常
   int i = 1/0;

   model.addAttribute("sysUserCriteria", sysUserCriteria);

   return "sysUser/list";
}

运行后结果如下图:
全局异常显示
自定义异常

/**
 * 系统用户 列表页
 *
 * @param model
 * @return
 */
@WebLog(channel = "web", name = "系统用户列表", action = "/sysUser", saveFlag = true)
@GetMapping("")
public String list(Model model) throws MyException {
   if (1 > 0){
      throw new MyException("10001", "审核异常", "审核信息缺失", "/person/check");
   }
   
   return "sysUser/list";
}

运行后结果如下图:
自定义异常显示


总结

一个问题的解决方案有千万种,选择适合自己的。
解决问题的资料不能单纯的拿来就用,改改为己所用。
办法总比困难多,加油,菜鸟。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编程火箭车

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

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

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

打赏作者

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

抵扣说明:

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

余额充值