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

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

当我们的后端应用出现异常时,通常会将异常状况包装之后再返回给调用方或者前端,在实际的项目中,不可能对每一个地方都做好异常处理,再优雅的代码也可能抛出异常,那么在 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为所有的 Controller 织入增强方法
  • @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

image.png


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);
}
复制代码

image.png


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 被我们捕获并返回堆栈信息

image.png


相关文章
|
开发框架 前端开发 JavaScript
ASP.NET Web Pages - 教程
ASP.NET Web Pages 是一种用于创建动态网页的开发模式,采用HTML、CSS、JavaScript 和服务器脚本。本教程聚焦于Web Pages,介绍如何使用Razor语法结合服务器端代码与前端技术,以及利用WebMatrix工具进行开发。适合初学者入门ASP.NET。
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
216 4
|
10月前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
357 20
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
784 7
Spring Boot 入门:简化 Java Web 开发的强大工具
|
网络安全 开发工具 数据安全/隐私保护
|
XML 安全 PHP
PHP与SOAP Web服务开发:基础与进阶教程
本文介绍了PHP与SOAP Web服务的基础和进阶知识,涵盖SOAP的基本概念、PHP中的SoapServer和SoapClient类的使用方法,以及服务端和客户端的开发示例。此外,还探讨了安全性、性能优化等高级主题,帮助开发者掌握更高效的Web服务开发技巧。
|
XML Java 网络架构
使用 Spring Boot 公开 SOAP Web 服务端点:详细指南
使用 Spring Boot 公开 SOAP Web 服务端点:详细指南
1408 0
|
监控 Java Maven
springboot学习二:springboot 初创建 web 项目、修改banner、热部署插件、切换运行环境、springboot参数配置,打包项目并测试成功
这篇文章介绍了如何快速创建Spring Boot项目,包括项目的初始化、结构、打包部署、修改启动Banner、热部署、环境切换和参数配置等基础操作。
1228 0
|
Java 应用服务中间件 Maven
传统maven项目和现在spring boot项目的区别
Spring Boot:传统 Web 项目与采用 Spring Boot 项目区别
700 0
传统maven项目和现在spring boot项目的区别
|
XML Java 数据库连接
创建springboot项目的基本流程——以宠物类别为例
创建springboot项目的基本流程——以宠物类别为例
236 0
创建springboot项目的基本流程——以宠物类别为例