Spring Gateway认证过滤器如何捕获500错误并返回Auth-Service原生响应
解决方案
核心原因
WebClient 默认的 retrieve() 方法会对所有 4xx、5xx 状态的响应直接抛出 WebClientResponseException 异常,中断正常的响应体解析流程,因此你无法直接获取用户服务返回的错误结构体。另外你自行调用 subscribe() 的写法不符合响应式编程规范,会脱离当前请求的上下文,导致异常无法被当前请求链路捕获。
实现代码
推荐使用 exchangeToMono 替代 retrieve(),该方法可以无视响应状态拿到完整的响应对象,自行处理成功/失败逻辑,无需提前定义错误响应Dto就能直接透传用户服务的原始返回:
@Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization"); // 增加Authorization头非空校验,避免空指针异常 if (authHeader == null || !authHeader.startsWith("Bearer ")) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } String[] parts = authHeader.split(" "); return webClientBuilder.build() .post() .uri("http://USER-SERVICE/user/validateToken?token=" + parts[1]) // exchangeToMono可拿到所有状态的完整响应 .exchangeToMono(response -> { // 2xx状态走正常认证成功逻辑 if (response.statusCode().is2xxSuccessful()) { return response.bodyToMono(UserDto.class) .map(userDto -> { exchange.getRequest() .mutate() .header("X-auth-user-id", String.valueOf(userDto.getId())); return exchange; }) .flatMap(chain::filter); } else { // 非2xx状态直接透传用户服务的原始响应 exchange.getResponse().setStatusCode(response.statusCode()); // 复制所有响应头 response.headers().asHttpHeaders().forEach((headerName, headerValues) -> { exchange.getResponse().getHeaders().addAll(headerName, headerValues); }); // 直接写入原始响应体,无需解析结构 return exchange.getResponse().writeWith(response.bodyToFlux(DataBuffer.class)); } }); }; }
可选的retrieve适配方案
如果你坚持使用 retrieve() 方法,也可以通过 onStatus 自定义错误处理逻辑,灵活度略低于上述方案:
.retrieve() // 匹配所有非2xx响应 .onStatus(status -> !status.is2xxSuccessful(), clientResponse -> clientResponse.bodyToMono(ApiExceptionDto.class) .flatMap(dto -> Mono.error(new BizException(dto))) ) .bodyToMono(UserDto.class) // 异常兜底处理 .onErrorResume(BizException.class, e -> { // 自行构造响应返回 })
内容的提问来源于stack exchange,提问作者Soios
相关产品推荐
相关产品推荐

