SpringBoot配置全局异常捕获

SpringBoot中自带的异常捕获机制返回的默认页面比较丑,对用户来说不够人性化。所以这篇文章来讲解SpringBoot钟自定义全局异常捕获。
本文的源码已经上传GitHub:https://github.com/haozz1994/exceptiondemo
主要讲解三种异常捕获形式:

  • 页面跳转异常
  • ajax异常
  • 统一返回异常的形式

分别来看三种形式的全局异常捕获:

1.页面跳转异常

首先新建一个异常:

package com.haozz.exceptiondemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author haozz
 * @date 2018/6/19 16:57
 * @description
 */
@Controller
@RequestMapping(value = "/err")
public class ErrorCtrl {

    @RequestMapping(value = "/error")
    public String error(){
        int a = 1/0;
        return "abcdefg";
    }
}

浏览器请求,会得到SpringBoot默认的异常页面:
SpringBoot配置全局异常捕获_第1张图片

新建异常处理工具类MyExceptionHandler:

package com.haozz.exceptiondemo.utils;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author haozz
 * @date 2018/6/19 17:16
 * @description
 */
@ControllerAdvice
public class MyExceptionHandler {

    public static final String ERROR_VIEW = "my_error";

    @ExceptionHandler(value = Exception.class)//指定拦截的异常
    public Object errorHandler(HttpServletRequest request, HttpServletResponse response,Exception e) throws Exception{
        e.printStackTrace();//打印异常信息
        ModelAndView mv = new ModelAndView();
        mv.addObject("exception",e);
        mv.addObject("url",request.getRequestURL());//发生异常的路径
        mv.setViewName(ERROR_VIEW);//指定发生异常之后跳转页面
        return mv;
    }
}

这里我们使用了@ControllerAdvice标注,可以将其理解为增强型控制器,想要了解关于其更多信息请自行百度。@ExceptionHandler标注用于指定需要拦截的异常。其余的代码没有需要过多解释的,目的就是在系统中发生了指定的异常时,进行捕获,并返回指定的异常处理路径。我们在resources/templates下新建my_error.html:


<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>程序员迷路了title>
head>
<body>
    <h1 style="color:red">程序员迷路了:h1>
    <div th:text="${url}">div>
    <div th:text="${exception.message}">div>
body>
html>

这里有一点需要强调:我之前新建的异常跳转页面取名为error.html,这样会报错EL1008L

org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'timestamp' cannot be found on object of type 'java.util.HashMap...

后来在这篇帖子中找到了解决办法:http://makaidong.com/FlyAway2013/104144_12644765.html,我的理解是,SpringBoot中有默认的异常跳转路径(可能命名为error),而且其中有timestamp的属性,而当我们发生异常指定其跳转至error.html时,系统会以为是要跳转至默认的异常处理路径,就会去加载timestamp属性,这时当然是加载不到的,因为我们并没有设置这个属性。所以,这里最好不要将自定义的异常跳转路径命名为error

我们再在浏览器中请求err/error:
SpringBoot配置全局异常捕获_第2张图片
得到了我们想要的结果,小伙们可以打断点看一下。当然这个只是一个测试页面,并不美观。正式环境中当然会有专业的美工和前端来设计报错页面。

2.ajax异常

如果出现ajax异常而不捕获的话,就会一直loading,用户体验更加不好。所以ajax请求也需要处理。

我们首先要将之前创建的MyExceptionHandler类删除掉,避免其产生影响(只需要将类头部的@ControllerAdvice标注和方法头部的@ExceptionHandler标注注释掉即可)

然后新建ajax形式的异常处理工具类,命名为MyAjaxExceptionHandler:

package com.haozz.thymeleafdemo.utils;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

/**
 * @author haozz
 * @date 2018/6/19 18:41
 * @description
 */
@RestControllerAdvice
public class MyAjaxExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public CommonRspVo defaultErrorHandler(HttpServletRequest request,Exception e)throws Exception{
        e.printStackTrace();
        CommonRspVo rsp = new CommonRspVo("555",e.getMessage());
        return rsp;
    }
}

这里和之前页面跳转异常的不同在于类头部的标注换成了@RestControllerAdvice,其他的地方基本一致。在这里,当ajax异常发生时,我使用了一个简单的包装类返回信息给前台。

在ErrorCtrl中新增两个方法:

//跳转到发生ajax异常页面
    @RequestMapping("/ajaxerror")
    public String ajaxerror(){
        return "thymeleaf/ajaxerror";
    }

    //发生ajax异常
    @RequestMapping("/getAjaxerror")
    public int getAjaxerror(){
        int a = 1/0;
        return a;
    }

其中ajaxerror方法用于跳转到前台,getAjaxerror方法用于接收前台ajax请求并制造异常。我们新建ajaxerror.html及ajaxerror.js:


<html lang="en" xmlns:th="http://www.springframework.org/schema/mvc">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>

    <script type="text/javascript" th:src="@{/js/jquery.min.js}">script>
head>
<body>

<h1>测试ajax错误异常h1>

<script type="text/javascript" th:src="@{/js/ajaxerror.js}">script>
body>
html>
$.ajax({
    url:"/err/getAjaxerror",
    type:"POST",
    async:false,
    success:function(data){
        debugger;
        if(data.code == "200"){
            alert("success!");
        }else {
            alert("发生异常: "+ data.message);
        }
    },
    error:function(){
        alert("error");
    }
})

在浏览器中发起请求,跳转至ajaxerror.html页面,立即向后台发起ajax请求,并发生1/0异常,这时会被自定义的MyAjaxExceptionHandler捕获到,然后将异常信息等返回前台。
SpringBoot配置全局异常捕获_第3张图片
SpringBoot配置全局异常捕获_第4张图片
SpringBoot配置全局异常捕获_第5张图片

3.统一异常处理

web页面跳转和ajax异常的统一异常处理也是非常简单的,主要就是整合一下前两步的思路。可以判断前端请求request是否是ajax请求,然后采取不同的措施。

我们还是使用第一步中使用的MyExceptionHandler异常处理助手类,将之前写好的errorHandler方法注释掉,并在其中添加如下两个方法:

@ExceptionHandler(value = Exception.class)
public Object errorHandler(HttpServletRequest request,HttpServletResponse response,Exception e)throws Exception{
        e.printStackTrace();
        if(isAjax(request)){//是ajax请求
            CommonRspVo rsp = new CommonRspVo("555",e.getMessage());
            return rsp;
        }else{//不是ajax请求
            ModelAndView mv = new ModelAndView();
            mv.addObject("exception",e);
            mv.addObject("url",request.getRequestURL());//发生异常的路径
            mv.setViewName(ERROR_VIEW);//指定发生异常之后跳转页面
            return mv;
        }
    }

    private boolean isAjax(HttpServletRequest request){//判断request是否是ajax请求
        return (request.getHeader("X-Requested-With") != null
                    && "XMLHttpRequest"
                        .equals( request.getHeader("X-Requested-With").toString()) );
    }

其中,isAjax方法用于判断HttpServletRequest是否是ajax请求。
这里需要注意的一点是,我们需要将MyExceptionHandler类头部的@ControllerAdvice标注修改为@RestControllerAdvice,否则在进行ajax异常捕获之后会报出TemplateInputException。
我们可以根据之前的err/error和err/ajaxerror路径分别进行页面跳转异常和ajax异常的测试,通过dubug调试可以更清楚的看到程序的执行过程。

你可能感兴趣的:(SpringBoot,异常处理,Exception,SpringBoot)