You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 08:24:02