SpringBoot实现优雅的全局异常处理

分类:
JAVA
标签:
SpringBoot
作者:
何鑫
创作时间:
2020/01/13 17:39:03

摘要:SpringBoot实现优雅的全局异常处理,轻松排查处理异常!

烦不胜烦的try...catch和不知所"错"的500页

在我曾经遇到的一些项目中,在写controller层的时候,对于ajax请求,总是得在代码前后加上try...catch,然后返回一些异常信息,打上日志,每个请求都得写,烦不胜烦,更可怕的是返回视图,甚至连try...catch都省了,一旦发生错误,看到的只能是500错误页,出错时只能一脸懵逼,根本不知道出了什么问题,非常难以排查,其实这就是当时搭框架和写代码时的考虑不周了,要知道对于程序员来说,工作的大部分内容都是在写BUG和处理BUG,如果在出现问题时,甚至不知道问题在哪里,那简直就是噩梦了。本文将就如何优雅的实现全局异常处理,谈谈自己的处理方式。

使用@ControllerAdvice实现全局异常处理

在SpringBoot中,可以使用@ControllerAdvice实现全局异常处理。

新建一个GlobalExceptionHandler类用于处理全局异常。这里省略SpringBoot工程的搭建,话不多说,直接上代码(使用了lombok,hutool,fastjson等工具):

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception error) {
        boolean ajax = HeaderUtil.isAjaxRequest(request);
        String errorMark = IdUtil.simpleUUID();
        if (ajax) {
            JSONObject json = new JSONObject();
            // 错误识别码
            json.put("error_code", 500);
            json.put("error_msg", "服务器内部错误,请复制错误识别码并联系站长!错误识别码:" + errorMark);
            log.error("【{}】系统错误:AJAX请求异常", errorMark);
            response.setStatus(500);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            try (PrintWriter out = response.getWriter()) {
                out.append(json.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        } else {
            // 页面请求
            ModelAndView mv = new ModelAndView();
            mv.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
            mv.addObject("errorMark", errorMark);
            mv.setViewName("common/500");
            log.error("【{}】系统错误:HTML请求异常", errorMark, error);
            return mv;
        }
    }

}

在类上加上@ControllerAdvice和@Slf4j注解,前者是springBoot提供的注解,后者是lombok提供的日志注解,可以省却繁琐的日志声明,推荐使用,可自行百度了解其功能。

@ExceptionHandler用于标记一个方法为异常处理方法,value值是需要捕获的具体异常,这里我们捕获了所有异常,该方法可以传入3个参数,分别为请求,响应及异常。对于一个请求来说,一般可能为异步或者直接页面视图请求,我们可以根据请求头判断其详细的请求类型,并做对应的处理。

新建一个随机标记,便于我们快速定位错误,这里我们使用了uuid,以最大程度避免重复。然后我们就可以根据请求的不同做不同的处理了。无论是页面请求还是异步请求,我们的思路是一致的,即返回错误信息并添加错误标记,错误标记会存在于日志之中,使用错误标记进行搜索便可以快速定位错误位置。

对于视图请求,我们建立一个视图模板(模板引擎使用thymeleaf):

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>500</title>
</head>
<body>
<div style="text-align: center">
    <h1>500 内部服务器错误 Internal Server Error</h1>
    <hr>
    <div>错误识别码:<span th:text="${errorMark}"></span>,请复制该识别码,并提交给站长以排除错误,谢谢!</div>
    <div><a href="/">返回首页>>></a></div>
</div>
</body>
</html>

我们把错误识别码展示给用户,并给予相应提示。

我们新建一个controller来测试一下:

@RequestMapping("/test")
public String test() {
    System.out.println(1/0);
    return "";
}

这里人为设置一个除0异常。启动SpringBoot,请求测试!

控制台我们搜索这个错误:

轻松找到异常,非常便捷!

我们再来测试一下Ajax请求,新建请求:

@RequestMapping("/test.ajax")
@ResponseBody
public String testAjax(HttpServletResponse response){
    System.out.println(1/0);
    return "success";
}

使用idea的rest client工具模拟请求:

            

        获得数据:

我们一样可以据此获取错误信息。

总结

在本文中,我们使用@ControllerAdvice实现全局异常的捕获和处理,并采用错误识别码的方式提高了定位问题的速度,不过需要注意的是404等客户端错误不会进入到该异常处理程序中,绝大多数情况下,我们也不会记录404的错误信息,所以应该也没什么影响。

好了,本文就讲到这里,由于本人能力所限,文章中难免可能出现一些错误,如果您有发现错误或者对文章有什么意见,欢迎在评论区给我留言,谢谢!

发表评论

温馨提示: 评论先审核后发布, 请勿发表不良言论

所有评论