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

Spring Cloud Gateway同Filter读写请求/响应体问题及实现示例

Spring Cloud Gateway装饰器方案无法读取响应体问题

我需要在同一个Spring Cloud Gateway过滤器中读取并改写HTTP请求/响应体,调研后选择了装饰器方案,但现在无法获取到响应体。

相关代码如下:

public class AuthorizationGatewayFilterFactory
        extends AbstractGatewayFilterFactory<AuthorizationGatewayFilterFactory.Config> {
    
    //...

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            LOG.info("Pre-processing");

            ServerHttpRequestDecorator loggingServerHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
                String requestBody = "";

                @Override
                public Flux<DataBuffer> getBody() {
                    return super.getBody().doOnNext(dataBuffer -> {
                        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
                            Channels.newChannel(byteArrayOutputStream)
                                    .write(dataBuffer.asByteBuffer().asReadOnlyBuffer());
                            requestBody = IOUtils.toString(byteArrayOutputStream.toByteArray(), "UTF-8");
                            LOG.info("requestBody: " + requestBody);
                        } catch (IOException e) {
                            //ignore
                        }
                    });
                }
            };
            
            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<? extends DataBuffer>) body;
                        return super.writeWith(fluxBody.map(dataBuffer -> {
                            byte[] content = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(content);
                            LOG.info("Response : {}", new String(content, StandardCharsets.UTF_8));
                            return bufferFactory.wrap(content);
                        }));
                    }
                    return super.writeWith(body);
                }
            }; 
            
            
            return chain.filter(exchange.mutate().request(loggingServerHttpRequestDecorator).response(decoratedResponse).build()).then(Mono.fromRunnable(() -> {
                LOG.info("Post-processing");
            }));
            
            
        };
    }
}

日志输出结果:

...AuthorizationGatewayFilterFactory : requestBody: HelloRequest
...AuthorizationGatewayFilterFactory : Post-processing

我发送的请求:

curl --location 'http://localhost:8080/users/test6' \
--header 'Content-Type: text/plain' \
--data 'HelloRequest'

被代理服务返回的响应为:HTTP 200 Hello2

请问为什么响应体没有被日志记录(后续我还需要改写响应体)?


更新内容

这里提供一个替代装饰器的最简示例,流程如下:

  • 客户端向网关发送POST http://localhost:8080/some/path请求,请求体为bonjour
  • 网关向代理服务发送POST http://localhost:8081/some/path请求,请求体改写为hello
  • 代理服务向网关返回响应体hello
  • 网关向客户端返回响应体改写为Bonjour

路由声明代码:

public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("routeId", r -> r.method(HttpMethod.POST).and().path("/some/path").filters(filtersToApply()).uri("http://localhost:8081"))
            .build();
}

应用的过滤器代码:

private Function<GatewayFilterSpec, UriSpec> filtersToApply() {
    return f -> {
        
        f.modifyRequestBody(String.class, String.class, (exchange, originalRequest) -> {
            log.info("Pre-processing");
            log.debug("originalRequest=" + originalRequest);
            final String newRequest = "Hello";
            log.debug("newRequest=" + newRequest);
            return Mono.just(newRequest);
        });
        
        f.modifyResponseBody(String.class, String.class, (exchange, originalResponse) -> {
            log.info("Post-processing");
            log.debug("Post-processing: originalResponse=" + originalResponse);
            final String newResponse = "Bonjour";
            log.debug("newResponse=" + newResponse);
            return Mono.just(newResponse);
        });
                    
        return f;
    };
}

向网关发送的请求:

curl --location 'http://localhost:8080/some/path' \
--header 'Content-Type: text/plain' \
--data 'bonjour'

网关日志结果:

INFO : Pre-processing
DEBUG : originalRequest=bonjour
DEBUG : newRequest=Hello
 INFO : Post-processing
DEBUG : Post-processing: originalResponse=hello
DEBUG : newResponse=Bonjour

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 05:35:15