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
相关产品推荐
相关产品推荐

