转载

191010-SpringBoot系列教程web篇之全局异常处理

当我们的后端应用出现异常时,通常会将异常状况包装之后再返回给调用方或者前端,在实际的项目中,不可能对每一个地方都做好异常处理,再优雅的代码也可能抛出异常,那么在Spring项目中,可以怎样优雅的处理这些异常呢?

本文将介绍一种全局异常处理方式,主要包括以下知识点

  • @ControllerAdvice Controller增强
  • @ExceptionHandler 异常捕获
  • @ResponseStatus 返回状态码
  • NoHandlerFoundException处理(404异常捕获)

I. 环境搭建

首先得搭建一个web应用才有可能继续后续的测试,借助SpringBoot搭建一个web应用属于比较简单的活;

创建一个maven项目,pom文件如下

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7</version>
    <relativePath/> <!-- lookup parent from update -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.45</version>
    </dependency>
</dependencies>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

依然是一般的流程,pom依赖搞定之后,写一个程序入口

/**
 * Created by @author yihui in 15:26 19/9/13.
 */
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

II. 异常处理

1. @ControllerAdvice

我们通常利用 @ControllerAdvice 配合注解 @ExceptionHandler 来实现全局异常捕获处理

@ControllerAdvice
@ExceptionHandler

下面我们通过实例进行功能演示

a. 异常捕获

我们定义两个异常捕获的case,一个是除0,一个是数组越界异常

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    public static String getThrowableStackInfo(Throwable e) {
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        e.printStackTrace(new java.io.PrintWriter(buf, true));
        String msg = buf.toString();
        try {
            buf.close();
        } catch (Exception t) {
            return e.getMessage();
        }
        return msg;
    }

    @ResponseBody
    @ExceptionHandler(value = ArithmeticException.class)
    public String handleArithmetic(HttpServletRequest request, HttpServletResponse response, ArithmeticException e)
            throws IOException {
        log.info("divide error!");
        return "divide 0: " + getThrowableStackInfo(e);
    }

    @ResponseBody
    @ExceptionHandler(value = ArrayIndexOutOfBoundsException.class)
    public String handleArrayIndexOutBounds(HttpServletRequest request, HttpServletResponse response,
            ArrayIndexOutOfBoundsException e) throws IOException {
        log.info("array index out error!");
        return "aryIndexOutOfBounds: " + getThrowableStackInfo(e);
    }
}

在上面的测试中,我们将异常堆栈返回调用方

b. 示例服务

增加几个测试方法

@Controller
@RequestMapping(path = "page")
public class ErrorPageRest {

    @ResponseBody
    @GetMapping(path = "divide")
    public int divide(int sub) {
        return 1000 / sub;
    }

    private int[] ans = new int[]{1, 2, 3, 4};

    @ResponseBody
    @GetMapping(path = "ary")
    public int ary(int index) {
        return ans[index];
    }
}

c. 测试说明

实例测试如下,上面我们声明捕获的两种异常被拦截并输出对应的堆栈信息;

但是需要注意

  • 404和未捕获的500异常 则显示的SpringBoot默认的错误页面;
  • 此外我们捕获返回的http状态码是200

191010-SpringBoot系列教程web篇之全局异常处理

2. @ResponseStatus

上面的case中捕获的异常返回的状态码是200,但是在某些case中,可能更希望返回更合适的http状态码,此时可以使用 ResponseStatus 来指定

使用方式比较简单,加一个注解即可

@ResponseBody
@ExceptionHandler(value = ArithmeticException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleArithmetic(HttpServletRequest request, HttpServletResponse response, ArithmeticException e)
        throws IOException {
    log.info("divide error!");
    return "divide 0: " + getThrowableStackInfo(e);
}

191010-SpringBoot系列教程web篇之全局异常处理

3. 404处理

通过 @ControllerAdvice 配合 @ExceptionHandler 可以拦截500异常,如果我希望404异常也可以拦截,可以如何处理?

首先修改配置文件 application.properties ,将 NoHandlerFoundException 抛出来

# 出现错误时, 直接抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
# 设置静态资源映射访问路径,下面两个二选一,
spring.mvc.static-path-pattern=/statics/**
# spring.resources.add-mappings=false

其次是定义异常捕获

@ResponseBody
@ExceptionHandler(value = NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handleNoHandlerError(NoHandlerFoundException e, HttpServletResponse response) {
    return "noHandlerFound: " + getThrowableStackInfo(e);
}

再次测试如下,404被我们捕获并返回堆栈信息

191010-SpringBoot系列教程web篇之全局异常处理

II. 其他

0. 项目

web系列博文

  • 190930-SpringBoot系列教程web篇之404、500异常页面配置
  • 190929-SpringBoot系列教程web篇之重定向
  • 190913-SpringBoot系列教程web篇之返回文本、网页、图片的操作姿势
  • 190905-SpringBoot系列教程web篇之中文乱码问题解决
  • 190831-SpringBoot系列教程web篇之如何自定义参数解析器
  • 190828-SpringBoot系列教程web篇之Post请求参数解析姿势汇总
  • 190824-SpringBoot系列教程web篇之Get请求参数解析姿势汇总
  • 190822-SpringBoot系列教程web篇之Beetl环境搭建
  • 190820-SpringBoot系列教程web篇之Thymeleaf环境搭建
  • 190816-SpringBoot系列教程web篇之Freemaker环境搭建

项目源码

  • 工程: https://github.com/liuyueyi/spring-boot-demo
  • 项目: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/209-web-error

1. 一灰灰Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

  • 一灰灰Blog个人博客 https://blog.hhui.top
  • 一灰灰Blog-Spring专题博客 http://spring.hhui.top

191010-SpringBoot系列教程web篇之全局异常处理

打赏 如果觉得我的文章对您有帮助,请随意打赏。

原文  http://spring.hhui.top/spring-blog/2019/10/10/191010-SpringBoot系列教程web篇之全局异常处理/
正文到此结束
Loading...