Java WebFilter响应式网关捕获全局Gateway路由异常

作者:技术拾光时间:2026-07-21 08:02:09浏览:510

SpringCloudGateway基于WebFlux,无法使用@ControllerAdvice。全局异常捕获可通过ErrorWebExceptionHandler实现框架兜底,捕获所有未处理异常并返回JSON响应;也可通过GlobalFilter结合onErrorResume拦截异常并终止链路,但需处理限流等组件异常穿透问题,避免循环异常。

在 Spring Cloud Gateway 响应式架构中,全局异常捕获仅有两种有效途径:使用 ErrorWebExceptionHandler 实现框架级兜底,或通过 GlobalFilter 在链路层面拦截。传统 Servlet 的 @ControllerAdvice 在 WebFlux 环境下无法生效——这一点需提前明确,避免走弯路。

Java WebFilter响应式网关捕获全局Gateway路由异常

之所以如此限定,是因为 Gateway 基于 WebFlux,不经过 Servlet 容器的 Filter 链,自然也无法使用 javax.servlet.Filter 捕获路由异常。真正有效的只有这两类,但它们的定位和适用场景差异较大,使用前需明确区分。

用 ErrorWebExceptionHandler 替换默认错误处理器

这是最推荐的全局兜底方案,没有之一。它能捕获所有未被上游过滤器处理的异常——包括路由匹配失败、转发超时、下游服务不可达、限流触发等,且优先级高于 Spring Boot 自带的默认实现。

  • 实现 ErrorWebExceptionHandler 接口,重写 handle() 方法
  • 添加 @Component 和 @Order(-1) 注解,确保优先加载
  • 先检查 response.isCommitted(),避免重复写入已提交的响应
  • 统一构造 JSON 响应体(例如 {"code":500,"message":"服务暂时不可用"}),并设置 Content-Type: application/json
  • 对常见异常分类处理:ResponseStatusException 取状态码,TimeoutException 返回 504,ConnectException 返回 503

用 GlobalFilter 拦截并提前终止异常链

该方案适合在请求进入路由前进行前置校验(如鉴权、参数校验),或在路由执行后捕获转发过程中抛出的异常。但需注意,它不能直接捕获网关内部组件(如断路器、限流器)抛出的原始异常——除非这些组件显式抛出并被链路传递下来。

  • 实现 GlobalFilter,使用 @Order(-2) 确保早于路由过滤器执行
  • 在 chain.filter(exchange).onErrorResume(...) 中捕获异常
  • 区分异常类型:ResponseStatusException 直接设置对应状态码,否则设 500 并记录日志
  • 调用 exchange.getResponse().setComplete() 终止后续过滤器执行
  • 避免在 onErrorResume 中抛出新异常——否则会再次触发 ErrorWebExceptionHandler,形成循环

注意限流、断路器等组件的异常穿透问题

像 RequestRateLimiter 或 Spring Cloud CircuitBreaker 抛出的异常,默认不会进入 GlobalFilter 的 onErrorResume,因为这些异常在独立过滤器中完成。如需统一处理,需换一种思路:

  • 在配置中显式指定回退逻辑,例如限流失败时返回自定义响应体(需配合 redis-rate-limiter 的 denyEmptyResponse 和自定义 RateLimiter 实现)
  • 让这些组件抛出标准 ResponseStatusException(如 new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS)),以便被 ErrorWebExceptionHandler 统一接管
  • 不建议在 GlobalFilter 中重写限流过滤器逻辑——容易破坏原有的执行顺序和状态一致性

不要用 @ControllerAdvice

这条必须单独强调。该注解依赖 Spring MVC 的同步请求上下文,在 WebFlux 响应式网关中完全无效。网关没有 Controller 层,也不经过 DispatcherHandler 的异常处理流程。即使添加 @RestControllerAdvice,也不会产生任何效果——它只是摆设。