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

Spring Cloud Gateway如何捕获下游404响应并修改为501状态码

问题原因

之前的自定义过滤器无法正确捕获下游404状态,核心问题有两点:

  • 代码在执行chain.filter()前就调用exchange.getResponse().getStatusCode()读取状态,此时请求还未转发到下游服务,响应还未生成,拿到的200是响应对象初始化的默认值,不是下游返回的真实状态。
  • 从实际返回给app1的响应结构看,下游app2的全局异常处理逻辑已经改写了原生404响应:将HTTP响应状态码强制设置为200,真实的404错误信息被封装到响应体的code、message字段中,直接读取HTTP响应头的状态码自然无法获取到404。
实现方案

方案1:网关层直接拦截未开发接口(最推荐,无需自定义代码)

当前仅endPoint3、endPoint4两个接口暂未实现,不需要把请求转发到app2再修改状态,可以直接在网关路由配置中拦截这部分路径,直接返回501,性能更高、维护更简单。等后续接口开发完成,把对应路径迁移到已上线的路由规则中即可。
配置示例:

# 已开发完成的接口路由,优先级更高
spring.cloud.gateway.routes[0].id=app2-completed
spring.cloud.gateway.routes[0].uri=lb://app2
spring.cloud.gateway.routes[0].predicates[0]=Path=/1/app2/get/endPoint1/**,/1/app2/get/endPoint2/**
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1

# 未开发的接口直接在网关返回501,不转发到下游
spring.cloud.gateway.routes[1].id=app2-unimplemented
spring.cloud.gateway.routes[1].uri=no://op
spring.cloud.gateway.routes[1].predicates[0]=Path=/1/app2/get/endPoint3/**,/1/app2/get/endPoint4/**
spring.cloud.gateway.routes[1].filters[0]=StripPrefix=1
spring.cloud.gateway.routes[1].filters[1]=SetStatus=501

方案2:全局自动捕获下游404转501(适配下游返回真实HTTP 404的场景)

如果需要全局处理所有下游服务返回的原生404响应,自定义全局过滤器时要注意两点:一是过滤器顺序必须在响应写回过滤器之前执行,二是要等下游响应头返回后再读取状态码。
代码示例:

@Component
public class NotFoundToNotImplementedFilter implements GlobalFilter, Ordered {

    @Override
    public int getOrder() {
        // 顺序要小于NettyWriteResponseFilter的默认顺序-1,确保在响应写回前修改状态
        return -2;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public HttpStatusCode getStatusCode() {
                HttpStatusCode originStatus = super.getStatusCode();
                // 下游返回404时统一改成501
                if (originStatus == HttpStatus.NOT_FOUND) {
                    return HttpStatus.NOT_IMPLEMENTED;
                }
                return originStatus;
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }
}

方案3:适配下游将404封装为HTTP 200响应体的场景

如果下游服务统一把错误码封装在响应体中、HTTP状态固定返回200(和当前遇到的情况一致),需要在过滤器中解析响应体内容,判断到是404错误时同时修改HTTP状态码和响应内容。
代码示例:

@Component
public class NotFoundToNotImplementedFilter implements GlobalFilter, Ordered {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    @Override
    public int getOrder() {
        return -2;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();

        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                        // 拼接完整响应体
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                        dataBuffers.forEach(dataBuffer -> {
                            byte[] content = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(content);
                            DataBufferUtils.release(dataBuffer); // 释放缓存避免内存泄漏
                            try {
                                outputStream.write(content);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        });
                        byte[] responseBytes = outputStream.toByteArray();
                        String responseStr = new String(responseBytes, StandardCharsets.UTF_8);

                        try {
                            JsonNode responseJson = OBJECT_MAPPER.readTree(responseStr);
                            // 判断响应体中code为404时修改状态
                            if (responseJson.has("code") && responseJson.get("code").asInt() == 404) {
                                setStatusCode(HttpStatus.NOT_IMPLEMENTED);
                                // 按需修改响应体内容
                                ObjectNode mutableJson = (ObjectNode) responseJson;
                                mutableJson.put("code", 501);
                                mutableJson.put("type", "Not Implemented");
                                responseBytes = OBJECT_MAPPER.writeValueAsBytes(mutableJson);
                            }
                        } catch (Exception e) {
                            // 响应解析失败时直接返回原内容
                        }

                        return bufferFactory.wrap(responseBytes);
                    }));
                }
                return super.writeWith(body);
            }
        };

        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }
}

内容的提问来源于stack exchange,提问作者luuzjnmmemjrwfwpsb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 20:18:19