Logo
Blog
Spring Cloud Gateway: How to Read Request Body Without Breaking Downstream Processing

Spring Cloud Gateway: How to Read Request Body Without Breaking Downstream Processing

Avatar
Neel Shah
September 17, 2025

Introduction

One of the most common challenges developers face with Spring Cloud Gateway is handling the request body inside filters.

If you’ve ever tried to read the body in a custom filter, you might have noticed something strange:
πŸ‘‰ After reading the body, downstream filters or services receive an empty body.
This happens because reactive streams in Spring WebFlux can only be consumed once.

❌ The Problem

Here’s a typical scenario:

java
@Component
@Order(1)
public class RequestValidationFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// ❌ PROBLEM: This consumes the request body stream
return request.getBody()
.collectList()
.flatMap(dataBuffers -> {
String requestBody = dataBuffers.stream()
.map(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);
return new String(bytes, StandardCharsets.UTF_8);
})
.collect(Collectors.joining());
if (requestBody.contains("forbidden")) {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
// ❌ Downstream services now receive an empty body
return chain.filter(exchange);
});
}
}

Problem: once the body is consumed here, it can’t be read again by downstream services.

βœ… The Solution: Cache and Reconstruct the Request Body

The fix involves a two-phase approach:

  1. Cache the request body early in the filter chain.
  2. Recreate a fresh body stream so downstream services still receive the complete request.

Step 1: Cache Request Body

We first create a filter that runs before everything else (@Order(-1)) and caches the request body.

java
@Component
@Order(-1) // Run first
public class CacheRequestBodyFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);
// Cache body in exchange attributes
exchange.getAttributes().put("CACHED_REQUEST_BODY", new String(bytes, StandardCharsets.UTF_8));
// Create reusable request decorator
ServerHttpRequest decoratedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return Flux.just(exchange.getResponse().bufferFactory().wrap(bytes));
}
};
return chain.filter(exchange.mutate().request(decoratedRequest).build());
});
}
}

Step 2: Use Cached Body for Validation

Later filters can simply read from cache instead of consuming the stream.

java
@Component
@Order(1) // Run after caching
public class RequestValidationFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String cachedBody = exchange.getAttribute("CACHED_REQUEST_BODY");
if (cachedBody != null && cachedBody.contains("forbidden")) {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}

πŸ”‘ Key Points

  • Cache early with @Order(-1) before other filters.
  • Store body in exchange.getAttributes() for later access.
  • Always call DataBufferUtils.release() to avoid memory leaks.
  • Use ServerHttpRequestDecorator to recreate the body stream for downstream services.

πŸš€ Benefits

  • Multiple filters can safely read the request body.
  • Downstream microservices receive the complete, unmodified body.
  • Solution stays within the reactive programming model.
  • Easy to extend for validation, logging, or request transformations.

Conclusion

Working with reactive request bodies in Spring Cloud Gateway can be tricky. By implementing a cache + decorator pattern, you can safely read the body multiple times without breaking downstream processing. This approach is ideal for:

  • Request validation
  • Logging & auditing
  • Security checks (e.g., forbidden content, payload scanning)

Contact Us

Thank you for reading our comprehensive guide on "Spring Cloud Gateway: How to Read Request Body Without Breaking Downstream Processing" We hope you found it insightful and valuable. If you have any questions, need further assistance, or are looking for expert support in developing and managing your projects. our team is here to help!

Reach out to us for Your Project Needs:

🌐 Website: https://www.prometheanz.com

πŸ“§ Email: [email protected]


Copyright Β© 2025 PrometheanTech. All Rights Reserved.