网关 Spring Cloud Gateway 统一全局异常处理


💨 作者:laker,因为喜欢LOL滴神faker,又是NBA湖人队🏀(laker)粉丝儿(主要是老詹的粉丝儿),本人又姓,故取笔名:laker
❤️喜欢分享自己工作中遇到的问题和解决方案以及一些读书笔记和心得分享
🌰本人创建了微信公众号【Java大厂面试官】,用于和大家交流分享
🏰 个人微信【lakernote】,加作者备注下暗号:cv之道


本文Spring Cloud Gateway 版本:2020.0.0

前言

Spring Cloud Gateway异常默认返回如下:

浏览器访问

在这里插入图片描述

PostMan访问

{
  "timestamp": "2021-01-06T04:56:53.092+00:00",
  "path": "/get2",
  "status": 404,
  "error": "Not Found",
  "message": null,
  "requestId": "b3e60f38-4"
}

看看如何实现统一异常处理。

Spring Cloud Gateway 中的全局异常处理不能直接使用 @ControllerAdvice,可以通过跟踪异常信息的抛出,找到对应的源码,自定义一些处理逻辑来匹配业务的需求。

网关是给接口做代理转发的,后端对应的是 REST API,返回数据格式是 JSON。如果不做处理,当发生异常时,Gateway 默认给出的错误信息是页面,不方便前端进行异常处理。

所以我们需要对异常信息进行处理,并返回 JSON 格式的数据给客户端。下面先看实现的代码,后面再跟大家讲一下需要注意的地方。

自定义异常处理逻辑

参考DefaultErrorWebExceptionHandler默认异常处理类

自定义异常处理逻辑,代码如下所示。

public class JsonExceptionHandler extends DefaultErrorWebExceptionHandler {

    public JsonExceptionHandler(ErrorAttributes errorAttributes, WebProperties.Resources resources,
                                ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resources, errorProperties, applicationContext);
    }
    /**
     * 获取异常属性
     */
    @Override
    protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
        int code = 500;
        Throwable error = super.getError(request);
        if (error instanceof org.springframework.cloud.gateway.support.NotFoundException) {
            code = 404;
        }
        return response(code, this.buildMessage(request, error));
    }

    /**
     * 指定响应处理方法为JSON处理的方法
     *
     * @param errorAttributes
     */
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    /**
     * 根据code获取对应的HttpStatus
     *
     * @param errorAttributes
     */
    @Override
    protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
        int statusCode = (int) errorAttributes.get("code");
        return HttpStatus.valueOf(statusCode);
    }

    /**
     * 构建异常信息
     *
     * @param request
     * @param ex
     * @return
     */
    private String buildMessage(ServerRequest request, Throwable ex) {
        StringBuilder message = new StringBuilder("Failed to handle request [");
        message.append(request.methodName());
        message.append(" ");
        message.append(request.uri());
        message.append("]");
        if (ex != null) {
            message.append(": ");
            message.append(ex.getMessage());
        }
        return message.toString();
    }

    /**
     * 构建返回的JSON数据格式
     *
     * @param status       状态码
     * @param errorMessage 异常信息
     * @return
     */
    public static Map<String, Object> response(int status, String errorMessage) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", status);
        map.put("message", errorMessage);
        map.put("data", null);
        return map;
    }
}

覆盖默认的配置

参考ErrorWebFluxAutoConfiguration

覆盖默认的配置,代码如下所示。

@Configuration
@EnableConfigurationProperties({ServerProperties.class,
        WebProperties.class})
public class ErrorHandlerConfiguration {


    private final ServerProperties serverProperties;

    public ErrorHandlerConfiguration(ServerProperties serverProperties) {
        this.serverProperties = serverProperties;
    }

    @Bean
    @Order(-1)
    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes,
                                                             org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
                                                             WebProperties webProperties, ObjectProvider<ViewResolver> viewResolvers,
                                                             ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext) {
        DefaultErrorWebExceptionHandler exceptionHandler = new JsonExceptionHandler(errorAttributes,
                resourceProperties.hasBeenCustomized() ? resourceProperties : webProperties.getResources(),
                this.serverProperties.getError(), applicationContext);
        exceptionHandler.setViewResolvers(viewResolvers.orderedStream().collect(Collectors.toList()));
        exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }
}

1. 异常时如何返回 JSON 而不是 HTML?

在 org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWeb-Exception-Handler 中的 getRoutingFunction() 方法就是控制返回格式的,源代码如下所示。

@Override
protected RouterFunction getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(acceptsTextHtml(), this::renderErrorView).andRoute(RequestPredicates.all(), this::renderErrorResponse);
}

这里优先是用 HTML 来显示的,如果想用 JSON 显示改动就可以了,具体代码如下所示。

protected RouterFunction getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(),this::renderErrorResponse);
}

2. getHttpStatus 需要重写

原始的方法是通过 status 来获取对应的 HttpStatus 的,具体代码如下所示。

protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
int statusCode = (int) errorAttributes.get(“status”);
return HttpStatus.valueOf(statusCode);
}

如果我们定义的格式中没有 status 字段的话,就会报错,因为找不到对应的响应码。要么返回数据格式中增加 status 子段,要么重写,在笔者的操作中返回的是 code,所以要重写,代码如下所示。

@Override
protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
int statusCode = (int) errorAttributes.get(“code”);
return HttpStatus.valueOf(statusCode);
}

验证

无论是网页还是PostMan

{
	"code": 404,
	"message": "Failed to handle request [GET http://localhost:8080/get]: 404 NOT_FOUND"
}

有些小伙伴回复说没起作用,可以参考下源码获取

  • 关注公众号【Java大厂面试官】,回复【gateway】

参考:

http://c.biancheng.net/view/5447.html

Spring Cloud 相关系列文章目录

网关服务

Spring Cloud Gateway


QQ群【837324215】
关注我的公众号【Java大厂面试官】,回复:常用工具资源等关键词(更多关键词,关注后注意提示信息)获取更多免费资料。

公众号也会持续输出高质量文章,和大家共同进步。

Spring Cloud Gateway 是一个高性能且轻量级的 API 网关,它是 Spring Cloud 家族的一员,用于路由、过滤和服务发现。在 Gateway 中,异常处理是一个关键功能,它可以帮助你在全局层面上捕获和处理请求过程中可能出现的各种错误。 在 Spring Cloud Gateway 中,你可以使用全局错误处理器(Global Error Handler)来统一处理所有未被路由到其他服务的异常。这样,即使内部微服务发生异常,用户也会看到一致的错误响应,而不是直接暴露服务内部的错误。 以下是实现统一异常处理的基本步骤: 1. 配置全局错误处理器:在 `application.yml` 或 `application.properties` 文件中添加全局异常处理器的配置,例如: ```yaml error: path: /error handler: exception: my.error.handler ``` 2. 创建全局错误处理器类:定义一个实现了 `ReactiveErrorWebExceptionHandler` 或其子类的类,并实现 `handle` 方法处理不同类型的错误。 ```java @Bean public GlobalErrorExceptionHandler myErrorHandler() { return new MyErrorExceptionHandler(); } public class MyErrorExceptionHandler implements ReactiveErrorWebExceptionHandler { @Override public Mono<ServerResponse> handle(ServerRequest request, Throwable ex) { // 这里可以根据异常类型、状态码等返回定制化的错误响应 return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR) .bodyValue("An unexpected error occurred: " + ex.getMessage()); } } ``` 3. 异常处理策略:你可以选择只处理特定类型的异常,或者使用 `@ExceptionHandler` 注解来处理特定的 HTTP 状态码。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lakernote

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

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

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

打赏作者

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

抵扣说明:

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

余额充值