I've implemented a Post filter in the spring cloud gateway. But I need the readable format (JSON Format of response body) before sending it to UI.
I'm getting exchange.getResponse(). (when i printed in console: org.springframework.http.server.reactive.ReactorServerHttpResponse@3891d61a
) But it is in the reactive object. I can't able to see the actual original response which is coming from API to post filter. I've searched numerous stackoverflow topics but couldn't get an actual solution. Please assist...
Asked
Active
Viewed 9,007 times
1

Saman Salehi
- 1,004
- 1
- 12
- 19

Dev-eloper
- 21
- 1
- 4
-
refer https://stackoverflow.com/a/71025638/8402445 or you can find in answers section – jbaddam17 Mar 15 '22 at 00:14
2 Answers
8
you can extract/read/modify/manipulate the request and response and their headers with the help of ServerHttpRequestDecorator
& ServerHttpResponseDecorator
see below
Note: i implemented GatewayFilter because i have this logic in gateway service level, if you want to modify at micro service level you can use WebFilter
import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.IOUtils;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import java.io.ByteArrayOutputStream;
import java.nio.channels.Channels;
import java.nio.charset.StandardCharsets;
@Configuration
@Log4j2
public class RequestResponseModifyFilter implements GatewayFilter/WebFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getPath().toString();
ServerHttpResponse response = exchange.getResponse();
ServerHttpRequest request = exchange.getRequest();
DataBufferFactory dataBufferFactory = response.bufferFactory();
// log the request body
ServerHttpRequest decoratedRequest = getDecoratedRequest(request);
// log the response body
ServerHttpResponseDecorator decoratedResponse = getDecoratedResponse(path, response, request, dataBufferFactory);
return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build());
}
private ServerHttpResponseDecorator getDecoratedResponse(String path, ServerHttpResponse response, ServerHttpRequest request, DataBufferFactory dataBufferFactory) {
return new ServerHttpResponseDecorator(response) {
@Override
public Mono<Void> writeWith(final Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
DefaultDataBuffer joinedBuffers = new DefaultDataBufferFactory().join(dataBuffers);
byte[] content = new byte[joinedBuffers.readableByteCount()];
joinedBuffers.read(content);
String responseBody = new String(content, StandardCharsets.UTF_8);//MODIFY RESPONSE and Return the Modified response
log.debug("requestId: {}, method: {}, url: {}, \nresponse body :{}", request.getId(), request.getMethodValue(), request.getURI(), responseBody);
return dataBufferFactory.wrap(responseBody.getBytes());
})).onErrorResume(err -> {
log.error("error while decorating Response: {}",err.getMessage());
return Mono.empty();
});
}
return super.writeWith(body);
}
};
}
private ServerHttpRequest getDecoratedRequest(ServerHttpRequest request) {
return new ServerHttpRequestDecorator(request) {
@Override
public Flux<DataBuffer> getBody() {
log.debug("requestId: {}, method: {} , url: {}", request.getId(), request.getMethodValue(), request.getURI());
return super.getBody().publishOn(Schedulers.boundedElastic()).doOnNext(dataBuffer -> {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
Channels.newChannel(byteArrayOutputStream).write(dataBuffer.asByteBuffer().asReadOnlyBuffer());
String requestBody = IOUtils.toString(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8.toString());//MODIFY REQUEST and Return the Modified request
log.debug("for requestId: {}, request body :{}", request.getId(), requestBody);
} catch (Exception e) {
log.error(e.getMessage());
}
});
}
};
}
@Override
public int getOrder() { return -2;}
}

jbaddam17
- 277
- 2
- 10
-
Thank you very much, I find out of the world util saw your answer. – nobjta_9x_tq Aug 04 '22 at 03:09
-
@jbaddam17 Tried this method but the response decorator is not called. Here is the link to the post https://stackoverflow.com/questions/76078243/spring-cloud-gateway-serverhttpresponsedecorator-writewith-not-called-on-receivi – Dame Lyngdoh Apr 22 '23 at 10:47
-
@jbaddam17 Using the above code, I am able to log the request body. But not the response body. Could you please help? – Ankitha J Aug 13 '23 at 05:22
1
https://github.com/einsteinarbert/spring-webfux-response-logging Beware about Security Chain, maybe that filter will override your filter.

nobjta_9x_tq
- 1,205
- 14
- 16