spring-boot-route(四)全局异常处理

本文介绍了如何在SpringBoot中优雅地实现全局异常处理,通过@ControllerAdvice和@ExceptionHandler注解进行异常捕获,并自定义响应状态码和消息的枚举类、响应数据实体类以及自定义异常。此外,还展示了如何处理查询结果为空、请求参数错误和其他未知异常的情况,并给出了接口测试示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在开发中,我们经常会使用try/catch块来捕获异常进行处理,如果有些代码中忘记捕获异常或者不可见的一些异常出现,就会响应给前端一些不友好的提示,这时候我们可以使用全局异常处理。这样就不用在代码中写那些烦人的try/catch块了,代码的可读性也会提高。

SpringBoot提供的的注解@ControllerAdvice表示开启全局异常捕获,在自定义的异常方法上使用ExceptionHandler来进行统一处理。

下面一起看看如何优雅的处理全局异常!

一 定义响应状态码及信息的枚举类

@Getter
public enum CodeEnum {
    
    SUCCESS(0,"请求成功"),
    ERROR(500,"未知异常"),
    ERROR_EMPTY_RESULT(1001,"查询结果为空"),
    ERROR_INCOMPLETE_RESULT(1002,"请求参数不全");
    
    private int code;
    private String message;
    CodeEnum(int code,String message){
        this.code = code;
        this.message = message;
    }
}

二 定义响应数据的实体类

@Slf4j
@Data
public class R<T> implements Serializable {

    private static final long serialVersionUID = 572235155491705152L;
    /**
     * 响应的状态码
     */
    private int code;
    /***
     * 响应的信息
     */
    private String message;
    /**
     * 响应数据
     */
    private T data;

    /**
     * 放入响应码并返回
     * @param code
     * @param msg
     * @return
     */
    public R fillCode(int code,String msg){
        this.code = code;
        this.message = msg;
        return this;
    }

    /**
     * 放入响应码并返回
     * @param codeEnum
     * @return
     */
    public R fillCode(CodeEnum codeEnum){
        this.code = codeEnum.getCode();
        this.message = codeEnum.getMessage();
        return this;
    }

    /**
     * 放入数据并响应成功状态
     * @param data
     * @return
     */
    public R fillData(T data){
        this.code = CodeEnum.SUCCESS.getCode();
        this.message = CodeEnum.SUCCESS.getMessage();
        this.data = data;
        return this;
    }
}

三 自定义两个异常

根据业务需求自定义异常,在本文中我定义了两个异常,分别用作响应结果为空时处理和请求参数错误时处理。

@Data
public class EmptyResutlException extends RuntimeException {

    private static final long serialVersionUID = -8839210969758687047L;
    private int code;
    private String message;

    public EmptyResutlException(CodeEnum codeEnum){
        this.code = codeEnum.getCode();
        this.message = codeEnum.getMessage();
    }
}
@Data
public class RequestParamException extends RuntimeException {

    private static final long serialVersionUID = 4748844811214637041L;
    private int code;
    private String message;

    public RequestParamException(CodeEnum codeEnum){
        this.code = codeEnum.getCode();
        this.message = codeEnum.getMessage();
    }
}

四 定义全局异常处理类

由于这里我想要响应的结果为实体类对象,因此我直接用@RestControllerAdvice来代替了@ControllerAdvice,这两个注解的差别跟@Controller@RestController一样,rest的响应体为json格式的数据。

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 查询结果为空时处理
     * @param e
     * @return
     */
    @ExceptionHandler(EmptyResutlException.class)
    public R emptyResultExceptionHandler(EmptyResutlException e){
        log.error("查询结果为空:{}",e.getMessage());
        R result = new R();
        result.fillCode(e.getCode(),e.getMessage());
        return result;
    }

    /**
     * 请求参数错误时处理
     * @param e
     * @return
     */
    @ExceptionHandler(RequestParamException.class)
    public R requestParamExceptionHandler(RequestParamException e){
        log.error("请求参数不合法:{}",e.getMessage());
        R result = new R();
        result.fillCode(e.getCode(),e.getMessage());
        return result;
    }

    /**
     * 处理其他异常
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public R exceptionHandler(Exception e){
        log.error("未知异常:{}",e.getMessage());
        R result = new R();
        result.fillCode(CodeEnum.ERROR);
        return result;
    }
}

五 自定义接口测试异常

@RestController
public class TestController {

    @GetMapping("getString")
    public R getString(String name){

        if(StringUtils.isEmpty(name)){
            throw new RequestParamException(1002,"请求参数name为空");
        }else if ("Java旅途".equals(name)) {
            // 这里没有查询操作,当请求参数是Java旅途的时候,模拟成查询结果为空
            throw new EmptyResutlException(1001,"查询结果为空");
        }
        // 这里模拟一下除自定义异常外的其他两种异常
        int i = 0;
        i = 5/i;
        return new R().fillData(name);
    }
}

在实际开发中可以自定义响应状态码的枚举类和自定义异常以满足需求。


本文示例代码已上传至github,点个star支持一下!

更多优质内容推荐访问 毕设侠

<think>我们正在讨论如何在Spring Boot 3中整合Gateway框架。Spring Cloud Gateway是一个基于Spring Framework 5,Project Reactor和Spring Boot 2.0的API网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。在Spring Boot 3中,由于Spring Boot 3要求Java 17及以上,并且基于Spring Framework 6,因此我们需要选择与Spring Boot 3兼容的Spring Cloud Gateway版本。 根据Spring Cloud的官方文档,Spring Cloud 2022.0.0(也称为Kilburn)及更高版本与Spring Boot 3.0兼容。因此,我们将使用Spring Cloud Gateway的相应版本。 整合步骤大致如下: 1. 创建Spring Boot 3项目 2. 添加Spring Cloud Gateway依赖 3. 配置路由规则 4. (可选)添加过滤器、断言等高级特性 下面我们逐步进行说明。 ### 1. 创建Spring Boot 3项目 可以使用Spring Initializr(https://start.spring.io/)创建项目,选择以下配置: - Project: Maven 或 Gradle - Language: Java - Spring Boot: 3.3.0(或更高) - Dependencies: 添加`Gateway`依赖 或者,如果已经有一个Spring Boot 3项目,可以直接在构建文件中添加依赖。 ### 2. 添加Spring Cloud Gateway依赖 在Maven项目中,需要在`pom.xml`中添加Spring Cloud的依赖管理以及Gateway的starter依赖。由于Spring Cloud Gateway属于Spring Cloud项目,因此需要先引入Spring Cloud的BOM(Bill of Materials)。 在`pom.xml`中添加如下内容: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2023.0.0</version> <!-- 使用与Spring Boot 3兼容的版本,注意:2023.0.0对应Spring Boot 3.1.x,但通常也兼容3.3.0 --> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- Spring Cloud Gateway --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> </dependencies> ``` 注意:Spring Cloud版本与Spring Boot版本的对应关系非常重要。根据Spring Boot 3.3.0,我们应使用Spring Cloud 2023.0.x版本。具体对应关系请参考[Spring Cloud官方文档](https://spring.io/projects/spring-cloud)。 ### 3. 配置路由规则 在`application.yml`(或`application.properties`)中配置路由规则。例如,我们配置一个简单的路由,将所有请求到`/service/**`的请求转发到`http://example.com`: ```yaml spring: cloud: gateway: routes: - id: example_route uri: http://example.com predicates: - Path=/service/** ``` 这里: - `id`:路由的唯一标识 - `uri`:目标服务的URI - `predicates`:断言条件,这里使用路径匹配 ### 4. 添加过滤器(可选) Gateway提供了丰富的过滤器,可以在请求转发前后进行一些处理。例如,我们可以添加一个添加请求头的过滤器: ```yaml spring: cloud: gateway: routes: - id: example_route uri: http://example.com predicates: - Path=/service/** filters: - AddRequestHeader=X-Request-Foo, Bar ``` 这个过滤器会在请求转发前添加一个请求头`X-Request-Foo: Bar`。 ### 5. 启动应用 完成配置后,启动Spring Boot应用。Gateway将会根据配置的路由规则进行请求转发。 ### 注意事项 - Spring Cloud Gateway底层使用Netty,因此不需要引入Web服务器(如Tomcat)的依赖。如果项目中同时引入了`spring-boot-starter-web`,需要排除,因为它们会冲突。通常,Gateway项目不需要`spring-boot-starter-web`。 - 如果使用服务发现(如Eureka),可以配置`uri`为`lb://service-id`,其中`service-id`是注册在服务发现中的服务名。 ### 示例代码 以下是一个简单的Spring Boot 3应用类: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ``` ### 测试 启动应用后,访问`http://localhost:8080/service/anything`,请求将被转发到`http://example.com/anything`。 ### 高级特性 Spring Cloud Gateway还支持: - 全局过滤器(Global Filters) - 熔断(Circuit Breaker) - 限流(Rate Limiting) - 重试(Retry) - 安全(Security)等 可以根据实际需求进行配置。 ### 引用说明 以上步骤参考了Spring Cloud Gateway官方文档[^1]。 §§相关问题§§ 1. Spring Cloud Gateway与Zuul有什么区别? 2. 如何在Spring Cloud Gateway中实现熔断功能? 3. 如何自定义全局过滤器? 4. 如何整合服务发现(如Eureka)到Spring Cloud Gateway? [^1]: Spring Cloud Gateway Documentation: https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/</think>### 在 Spring Boot 3 中整合 Gateway 框架的步骤 #### 1. **创建 Spring Boot 3 项目** 使用 Spring Initializr 创建项目: - 选择 **Spring Boot 3.3.0+**(需 Java 17+) - 依赖项:添加 `Gateway` 和 `Spring Web`(可选,但 Gateway 默认使用 Netty 非阻塞服务器) ```xml <!-- Maven 依赖示例 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> ``` #### 2. **配置 Spring Cloud 版本** 在 `pom.xml` 中指定与 Spring Boot 3 兼容的 Spring Cloud 版本(**2022.0.0+**): ```xml <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2023.0.0</version> <!-- 推荐版本 --> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` #### 3. **配置路由规则** 在 `application.yml` 中定义路由: ```yaml spring: cloud: gateway: routes: - id: user_service_route uri: http://localhost:8081 # 目标服务地址 predicates: - Path=/api/users/** # 匹配路径 filters: - StripPrefix=1 # 移除路径前缀 /api ``` - **路由组件**: - `predicates`:定义匹配条件(如路径、Header、方法等)。 - `filters`:添加请求/响应处理逻辑(如重写路径、限流等)。 #### 4. **自定义全局过滤器(可选)** 实现 `GlobalFilter` 接口,例如添加认证逻辑: ```java @Component public class AuthFilter implements GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getHeaders().getFirst("Authorization"); if (token == null) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); // 放行请求 } } ``` #### 5. **整合服务发现(如 Nacos/Eureka)** 若需动态路由,添加服务发现依赖: ```xml <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> ``` 配置动态路由: ```yaml spring: cloud: gateway: discovery: locator: enabled: true # 启用服务发现 routes: - id: order_service uri: lb://order-service # lb:// + 服务名 predicates: - Path=/api/orders/** ``` #### 6. **启动应用** 主类无需特殊注解: ```java @SpringBootApplication public class GatewayApp { public static void main(String[] args) { SpringApplication.run(GatewayApp.class, args); } } ``` #### 7. **验证路由** 访问 `http://localhost:8080/api/users/1`,请求将被路由到 `http://localhost:8081/users/1`。 ### ⚠️ 注意事项 1. **依赖冲突**:确保移除 `spring-boot-starter-web`(Gateway 使用 Netty,与 Tomcat 冲突)。 2. **版本兼容性**:Spring Cloud `2023.0.0` 需匹配 Spring Boot `3.1.x+`[^1]。 3. **性能调优**:在高并发场景下,可通过 `reactor-netty` 配置优化线程模型。 ### 典型应用场景 - **统一入口**:聚合微服务 API。 - **鉴权与限流**:通过过滤器实现统一安全控制。 - **灰度发布**:基于权重的路由分流。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java旅途

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值