Spring Cloud Gateway全局通用异常处理的实现

为什么需要全局异常处理

在传统 Spring Boot 应用中, 我们 @ControllerAdvice 来处理全局的异常,进行统一包装返回

// 摘至 spring cloud alibaba console 模块处理
@ControllerAdvice
public class ConsoleExceptionHandler {

  @ExceptionHandler(AccessException.class)
  private ResponseEntity<String> handleAccessException(AccessException e) {
    return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg());
  }
}

例如: ③ 处应用调用数据库异常,通过 @ControllerAdvice 包装异常请求响应给客户端

但在微服务架构下, 例如 ② 处 网关调用业务微服务失败(转发失败、调用异常、转发失败),在应用设置的 @ControllerAdvice 将失效,因为流量根本没有转发到应用上处理。

如上图: 模拟所有路由断言都不匹配 404 , 和 spring boot 默认保持一致的错误输出页面。 显然我们在网关同样配置 @ControllerAdvice 是不能解决问题,因为 spring cloud gateway 是基于 webflux 反应式编程。

解决方法

默认处理流程

ExceptionHandlingWebHandler 作为 spring cloud gateway 最核心 WebHandler 的一部分会进行异常处理的过滤

public class ExceptionHandlingWebHandler extends WebHandlerDecorator {
  @Override
  public Mono<Void> handle(ServerWebExchange exchange) {
    Mono<Void> completion;
    try {
      completion = super.handle(exchange);
    }
    catch (Throwable ex) {
      completion = Mono.error(ex);
    }

   // 获取全局的 WebExceptionHandler 执行
    for (WebExceptionHandler handler : this.exceptionHandlers) {
      completion = completion.onErrorResume(ex -> handler.handle(exchange, ex));
    }
    return completion;
  }
}

默认实现 DefaultErrorWebExceptionHandler

public class DefaultErrorWebExceptionHandler {

  @Override
  protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
   // 根据客户端 `accpet` 请求头决定返回什么资源,如上浏览器返回的是 页面
    return route(acceptsTextHtml(), this::renderErrorView).andRoute(all(), this::renderErrorResponse);
  }
}
// 模拟指定 `accpet` 情况
curl --location --request GET 'http://localhost:9999/adminx/xx' \ 18:09:23
   --header 'Accept: application/json'
{"timestamp":"2020-05-24 18:09:24","path":"/adminx/xx","status":404,"error":"Not Found","message":null,"requestId":"083c48e3-2"}⏎

重写 ErrorWebExceptionHandler

/**
 * @author lengleng
 * @date 2020/5/23
 * <p>
 * 网关异常通用处理器,只作用在webflux 环境下 , 优先级低于 {@link ResponseStatusExceptionHandler} 执行
 */
@Slf4j
@Order(-1)
@RequiredArgsConstructor
public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler {
  private final ObjectMapper objectMapper;

  @Override
  public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
    ServerHttpResponse response = exchange.getResponse();

    if (response.isCommitted()) {
      return Mono.error(ex);
    }

    // header set
    response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
    if (ex instanceof ResponseStatusException) {
      response.setStatusCode(((ResponseStatusException) ex).getStatus());
    }

    return response
        .writeWith(Mono.fromSupplier(() -> {
          DataBufferFactory bufferFactory = response.bufferFactory();
          try {
            return bufferFactory.wrap(objectMapper.writeValueAsBytes(R.failed(ex.getMessage())));
          } catch (JsonProcessingException e) {
            log.warn("Error writing response", ex);
            return bufferFactory.wrap(new byte[0]);
          }
        }));
  }
}

总结

  • 重写的 DefaultErrorWebExceptionHandler 优先级一定要小于内置 ResponseStatusExceptionHandler 经过它处理的获取对应错误类的 响应码
  • 其他扩展 可以参考 SentinelBlockExceptionHandler sentinel 整合网关的处理,不过整体和默认的异常处理没有什么区别
  • 基础环境说明:Spring Cloud Hoxton.SR4 & Spring Boot 2.3.0
  • 具体实现代码参考:https://gitee.com/log4j/pig

到此这篇关于Spring Cloud Gateway全局通用异常处理的实现的文章就介绍到这了,更多相关Spring Cloud Gateway全局异常内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2020-05-26

详解SpringCloud Finchley Gateway 统一异常处理

SpringCloud Finchley Gateway 统一异常处理 全文搜索[@@]搜索重点内容标记 1 . 问题:使用SpringCloud Gateway时,会出现各种系统级异常,默认返回HTML. 2 . Finchley版本的Gateway,使用WebFlux形式作为底层框架,而不是Servlet容器,所以常规的异常处理无法使用 翻阅源码,默认是使用DefaultErrorWebExceptionHandler这个类实现结构如下: 可以实现参考DefaultErrorWebExcep

Spring Cloud Gateway全局异常处理的方法详解

前言 Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式.Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代Netflix ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等

Spring Cloud GateWay 路由转发规则介绍详解

Spring在因Netflix开源流产事件后,在不断的更换Netflix相关的组件,比如:Eureka.Zuul.Feign.Ribbon等,Zuul的替代产品就是SpringCloud Gateway,这是Spring团队研发的网关组件,可以实现限流.安全认证.支持长连接等新特性. Spring Cloud Gateway Spring Cloud Gateway是SpringCloud的全新子项目,该项目基于Spring5.x.SpringBoot2.x技术版本进行编写,意在提供简单方便.可

Spring Boot读取resources目录文件方法详解

这篇文章主要介绍了Spring Boot读取resources目录文件方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在Java编码过程中,我们常常希望读取项目内的配置文件,按照Maven的习惯,这些文件一般放在项目的src/main/resources下,因此,合同协议PDF模板.Excel格式的统计报表等模板的存放位置是resources/template/test.pdf,下面提供两种读取方式,它们分别在windows和Linux

Spring Boot自定义错误视图的方法详解

Spring Boot缺省错误视图解析器 Web应用在处理请求的过程中发生错误是非常常见的情况,SpringBoot中为我们实现了一个错误视图解析器(DefaultErrorViewResolver).它基于一些常见的约定,尝试根据HTTP错误状态码解析出错误处理视图.它会在目录/error下针对提供的HTTP错误状态码搜索模板或者静态资源,比如,给定了HTTP状态码404,它会尝试搜索如下模板或者静态资源: /<templates>/error/404.<ext> - 这里<

spring cloud gateway 全局过滤器的实现

全局过滤器作用于所有的路由,不需要单独配置,我们可以用它来实现很多统一化处理的业务需求,比如权限认证,IP访问限制等等. 接口定义类:org.springframework.cloud.gateway.filter.GlobalFilter public interface GlobalFilter { Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain); } gateway自带的GlobalFilte

Spring cloud Feign 深度学习与应用详解

简介 Spring Cloud Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单.Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数.格式.地址等信息.Feign会完全代理HTTP请求,开发时只需要像调用方法一样调用它就可以完成服务请求及相关处理.开源地址:https://github.com/OpenFeign/feign.Feign整合了Ribbon负载和Hystrix熔断,可以不再需要显式地

Spring Cloud 的 Hystrix.功能及实践详解

一.概述 在微服务架构中,我们将系统拆分成了很多服务单元,各单元的应用间通过服务注册与订阅的方式互相依赖.由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身间题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会因等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪. 所以我们引入了断路器,类似于物理上的电路,当电流过载时,就断开电路,就是我们俗称的"跳闸".同理,服务间的调

Spring Boot Thymeleaf实现国际化的方法详解

前言 开发传统Java WEB工程时,我们可以使用JSP页面模板语言,但是在SpringBoot中已经不推荐使用了.SpringBoot支持如下页面模板语言 Thymeleaf FreeMarker Velocity Groovy JSP 上面并没有列举所有SpringBoot支持的页面模板技术.其中Thymeleaf是SpringBoot官方所推荐使用的,下面来谈谈Thymeleaf实现应用国际化方法. ps:当然现在开发基本上是前后端分离了,但是难免需要维护遗留项目或没有条件前后端分离的团队

spring boot 防止重复提交实现方法详解

本文实例讲述了spring boot 防止重复提交实现方法.分享给大家供大家参考,具体如下: 服务器端实现方案:同一客户端在2秒内对同一URL的提交视为重复提交 上代码吧 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.or