Spring Cloud Gateway中RouterFunction错误处理与WebClient异常透传咨询
问题1:WebClient异常透传实现标准化错误返回
实现步骤:
- 自定义异常类,用来存储下游服务返回的状态码、错误信息、时间戳字段:
// 自定义网关API异常,透传下游错误信息 public class GatewayApiException extends RuntimeException { private final int statusCode; private final String message; private final String timestamp; public GatewayApiException(int statusCode, String message, String timestamp) { this.statusCode = statusCode; this.message = message; this.timestamp = timestamp; } // 此处补充三个字段的getter方法 }
- 改造WebClient的状态异常处理逻辑,覆盖所有4xx、5xx错误场景,抛出上述自定义异常:
public Mono<RestNodeCreate> createNode(Mono<RestNodeCreate> node, String token) { return webClient .post() .uri("/nodes") .contentType(MediaType.APPLICATION_JSON) .header(HttpHeaders.AUTHORIZATION, token) .body(node, RestNodeCreate.class) .retrieve() // 捕获所有4xx、5xx错误状态 .onStatus(HttpStatus::isError, response -> { int status = response.statusCode().value(); // 如果下游响应头带时间戳可以直接提取,没有就网关侧生成 String timestamp = response.headers().getFirst("timestamp"); String finalTimestamp = timestamp == null ? LocalDateTime.now().toString() : timestamp; return response.bodyToMono(String.class) .flatMap(errorBody -> Mono.error(new GatewayApiException(status, errorBody, finalTimestamp))); }) .bodyToMono(RestNodeCreate.class); }
- 改造Handler层捕获异常,构造符合要求的标准化响应:
public Mono<ServerResponse> createNodes(ServerRequest request) { return request.bodyToMono(RestNodeCreate.class) .flatMap(newNodeCreate -> { if (newNodeCreate.getAccessMethod().equals("TCPIP")) { return oldCatalog.createNode(Mono.just(newNodeCreate), getTokenFromRequest(request)); } else { return builNodesToNewCatalog(Mono.just(newNodeCreate)) .flatMap(newNodeCreate1 -> newCatalog.createNode(Mono.just(newNodeCreate1))); } }) .flatMap(o -> ServerResponse.ok().body(Mono.just(o), Object.class)) // 新增异常处理逻辑,返回标准错误格式 .onErrorResume(GatewayApiException.class, e -> { Map<String, Object> errorResult = new HashMap<>(); errorResult.put("timestamp", e.getTimestamp()); errorResult.put("statuscode", e.getStatusCode()); errorResult.put("message", e.getMessage()); return ServerResponse.status(e.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .bodyValue(errorResult); }); }
如果有多个端点需要统一错误处理,可以直接实现WebExceptionHandler配置全局异常处理器,不需要每个Handler单独加异常处理逻辑。
问题2:Spring Cloud Gateway中能否使用阻塞的RestTemplate和同步@RestController
不建议也不推荐直接使用:
- Spring Cloud Gateway底层基于Spring WebFlux + Netty非阻塞架构设计,同步
@RestController是Spring MVC的Servlet栈组件,和WebFlux环境默认冲突,就算强制切换到Servlet运行模式,也会完全丧失SCG的非阻塞高吞吐优势。 - 直接在WebFlux环境调用
RestTemplate等阻塞API,会占用Netty的事件循环线程,导致请求堆积、吞吐量暴跌,严重时会触发服务雪崩。
如果必须使用阻塞逻辑,需要手动将阻塞操作包裹在Mono.defer()中,并且通过publishOn(Schedulers.boundedElastic())切换到专门的阻塞线程池执行,但这种方案仅适合低频场景,不推荐在网关层大量使用。如果你的业务逻辑以同步阻塞为主,更建议选择Zuul 1等基于阻塞IO设计的网关组件。
内容的提问来源于stack exchange,提问作者kamilguti
相关产品推荐
相关产品推荐

