SpringBoot优雅的全局异常处理

kenx
• 阅读 1944

前言

在日常项目开发中,异常是常见的,但是如何更高效的处理好异常信息,让我们能快速定位到BUG,是很重要的,不仅能够提高我们的开发效率,还能让你代码看上去更舒服,SpringBoot的项目已经有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理。

SpringBoot默认的错误处理机制

返回错误页面

默认返回 Whitelabel Error Page页面的样式太单调,用户体验不好。 SpringBoot优雅的全局异常处理 如 果 我 们 需 要 将 所 有 的 异 常 同 一 跳 转 到 自 定 义 的 错 误 页 面 , 需 要 再 src/main/resources/templates 目录下创建 error.html 页面。

注意:名称必须叫 error

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
        <!--SpringBoot默认存储异常信息的key为exception-->
    <span th:text="${exception}" />
</body>
</html>

返回json格式api

Json格式的结果字符串不统一,与前端人员约定统一格式不一致 SpringBoot优雅的全局异常处理

源码分析

SpringBoot在页面 发生异常的时候会自动把请求转到/error,SpringBoot内置了一个BasicErrorController对异常进行统一的处理,当然也可以自定义这个路径

SpringBoot优雅的全局异常处理

@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);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = this.getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity(status);
        } else {
            Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
            return new ResponseEntity(body, status);
        }
    }

我们可以看到刚好对照两个方法一个返回错误页面,一个返回错误字符,默认错误路径是/error如果有自定义就用自定义的

server.error.path=/custom/error

自定义错误处理

SpringBoot提供了ErrorAttribute类型 自定义ErrorAttribute类型的bean还是默认的两种响应方式,只不过改变了响应内容项而已

package cn.soboys.core;


import cn.hutool.core.bean.BeanUtil;
import cn.soboys.core.ret.Result;
import cn.soboys.core.ret.ResultCode;
import cn.soboys.core.ret.ResultResponse;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/18 14:14
 * 全局错误
 */
@Component
public class GlobalErrorHandler extends DefaultErrorAttributes {


    /**
     * 自定义错误返回页面
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @return
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        return super.resolveException(request, response, handler, ex);
    }

    /**
     * 自定义错误返回格式
     *
     * @param webRequest
     * @param options
     * @return
     */
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
        Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
        Result result = ResultResponse.failure(ResultCode.NOT_FOUND, errorAttributes.get("path"));
        Map map = BeanUtil.beanToMap(result, true, true);
        return map;
    }
}

自定义业务异常类

继承RuntimeException

package cn.soboys.core.authentication;

import cn.soboys.core.ret.ResultCode;
import lombok.Data;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/22 13:58
 * 认证异常
 */
@Data
public class AuthenticationException extends RuntimeException {

    public AuthenticationException(String message) {
        super(message);
    }

}

全局捕获异常

通过SpringBoot提供的@RestControllerAdvice@ControllerAdvice 结合@ExceptionHandler使用

@RestControllerAdvice@ControllerAdvice区别和@RestController,@Controller一样如果想返回json格式也可以单独使用@ResponseBody注解在方法上

需要捕获什么异常通过@ExceptionHandler来指定对应异常类就可以了这里原则是按照从小到大异常进行依次执行

通俗来讲就是当小的异常没有指定捕获时,大的异常包含了此异常就会被执行比如Exception 异常包含了所有异常类,是所有异常超级父类,当出现没有指定异常时此时对应捕获了Exception异常的方法会执行

@ExceptionHandler注解处理异常

@Controller
public class DemoController {
    @RequestMapping("/show")
    public String showInfo() {
        String str = null;
        str.length();
        return "index";
    }

    @RequestMapping("/show2")
    public String showInfo2() {
        int a = 10 / 0;
        return "index";
    }

    /**
     * java.lang.ArithmeticException 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视
     * 图的指定 参数 Exception e:会将产生异常对象注入到方法中
     */
    @ExceptionHandler(value = { java.lang.ArithmeticException.class })
    public ModelAndView arithmeticExceptionHandler(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("error", e.toString());
        mv.setViewName("error1");
        return mv;
    }

    /**
     * java.lang.NullPointerException 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视
     * 图的指定 参数 Exception e:会将产生异常对象注入到方法中
     */
    @ExceptionHandler(value = { java.lang.NullPointerException.class })
    public ModelAndView nullPointerExceptionHandler(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("error", e.toString());
        mv.setViewName("error2");
        return mv;
    }
}

优点:可以自定义异常信息存储的key,自定义跳转视图的名称

缺点:需要编写大量的异常处理方法,不能跨controller,如果两个controller中出现同样的异常,需要重新编写异常处理的方法

@ControllerAdvice+@ExceptionHandler 注解处理异常

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/17 20:19
 * 全局异常统一处理
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
 /**
     * 认证异常
     * @param e
     * @return
     */
    @ExceptionHandler(AuthenticationException.class)
    public Result UnNoException(AuthenticationException e) {
        return ResultResponse.failure(ResultCode.UNAUTHORIZED,e.getMessage());
    }

    /**
     *
     * @param e 未知异常捕获
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Result UnNoException(Exception e) {
        return ResultResponse.failure(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage());
    }
}

优点:可以自定义异常信息存储的key,自定义跳转视图的名称,跨controller统一拦截统一捕获,一般都是使用这种

关注公众号猿人生了解更多好文 SpringBoot优雅的全局异常处理

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
京东云开发者 京东云开发者
11个月前
一站式统一返回值封装、异常处理、异常错误码解决方案—最强的Sping Boot接口优雅响应处理器 | 京东云技术团队
GracefulResponse是一个SpringBoot体系下的优雅响应处理器,提供一站式统一返回值封装、异常处理、异常错误码等功能。使用GracefulResponse进行web接口开发不仅可以节省大量的时间,还可以提高代码质量,使代码逻辑更清晰。
Stella981 Stella981
2年前
Spring Cloud Gateway 全局通用异常处理
为什么需要全局异常处理在传统SpringBoot应用中,我们@ControllerAdvice来处理全局的异常,进行统一包装返回//摘至springcloudalibabaconsole模块处理@ControllerAdvicepublicclassConsol
Stella981 Stella981
2年前
Spring Boot 2.x(七):全局异常处理
前言异常的处理在我们的日常开发中是一个绕不过去的坎,在SpringBoot项目中如何优雅的去处理异常,正是我们这一节课需要研究的方向。异常的分类在一个SpringBoot项目中,我们可以把异常分为两种,第一种是请求到达Controller层之前,第二种是到达Controller层之后项目代码中发生的错误。而第一种又可
Stella981 Stella981
2年前
SpringBoot项目中自定义404页面
1错误处理有原理分析使用SpringBoot创建的web项目中,当我们请求的页面不存在(http状态码为404),或者服务器发生异常(http状态码一般为500)时,SpringBoot就会给我们返回错误信息。也就是说,在SpringBoot的web项目中,会自动创建一个/error的错误接口,来返回错误信息。但是针对不同的访问方式,会有以下两种
Wesley13 Wesley13
2年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
Stella981 Stella981
2年前
Spring Boot @ControllerAdvice+@ExceptionHandler处理controller异常
需求:  1.springboot 项目restful 风格统一放回json  2.不在controller写trycatch代码块简洁controller层  3.对异常做统一处理,同时处理@Validated校验器注解的异常方法:  @ControllerAdvice注解定义全局异常处理类@ControllerAdvice
Wesley13 Wesley13
2年前
初探 Objective
作者:Cyandev,iOS和MacOS开发者,目前就职于字节跳动0x00前言异常处理是许多高级语言都具有的特性,它可以直接中断当前函数并将控制权转交给能够处理异常的函数。不同语言在异常处理的实现上各不相同,本文主要来分析一下ObjectiveC和C这两个语言。为什么要把ObjectiveC和
Easter79 Easter79
2年前
SpringBoot项目中自定义404页面
1错误处理有原理分析使用SpringBoot创建的web项目中,当我们请求的页面不存在(http状态码为404),或者服务器发生异常(http状态码一般为500)时,SpringBoot就会给我们返回错误信息。也就是说,在SpringBoot的web项目中,会自动创建一个/error的错误接口,来返回错误信息。但是针对不同的访问方式,会有以下两种
小万哥 小万哥
11个月前
C++异常和错误处理机制:如何使您的程序更加稳定和可靠
在C编程中,异常处理和错误处理机制是非常重要的。它们可以帮助程序员有效地处理运行时错误和异常情况。本文将介绍C中的异常处理和错误处理机制。什么是异常处理?异常处理是指在程序执行过程中发生异常或错误时,程序能够捕获并处理这些异常或错误的机制。例如,当