tnaluat tnaluat
tnaluat tnaluat

Reputation: 43

Spring webflux webfilter removes the body from the request

I have a filter created where I access the body of the payload and do some logic on it (for now let's say I log the body). In the last step, I return Mono but when the request proceeds through the controller to services it throws a bad request error that the body is missing.

Code for the filter:

    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
        HttpHeaders headers = serverWebExchange.getRequest().getHeaders();
        String domain = headers.getFirst("domain");
        return serverWebExchange.getRequest().getBody()
                .single()
                .flatMap(body -> Mono.just(parseBody(body)))
                .flatMap(s -> webFilterChain.filter(serverWebExchange));
}

private String parseBody(DataBuffer fbody) {
        System.out.println("parsing body");
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            Channels.newChannel(baos).write(fbody.asByteBuffer().asReadOnlyBuffer());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return baos.toString(StandardCharsets.UTF_8);
}

The error: org.springframework.web.server.ServerWebInputException: 400 BAD_REQUEST "Request body is missing"

What can cause this behavior?

Upvotes: 4

Views: 2608

Answers (2)

Mradul Pandey
Mradul Pandey

Reputation: 2054

We can modify ServerWebExchange and construct new request to continue:

You can replace " System.out.println("Request Body in Filter: " + requestBody);" with your custom logic.


import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;

@Component
public class MyWebFilter implements WebFilter {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    ServerHttpRequest request = exchange.getRequest();
    Flux<DataBuffer> body = request.getBody();

    return DataBufferUtils.join(body)
        .map(dataBuffer -> {
            byte[] bytes = new byte[dataBuffer.readableByteCount()];
            dataBuffer.read(bytes);
            DataBufferUtils.release(dataBuffer);
            return bytes;
        })
        .map(bytes -> {
            String requestBody = new String(bytes, StandardCharsets.UTF_8);
            System.out.println("Request Body in Filter: " + requestBody);
            ServerHttpRequest modifiedRequest = new ServerHttpRequestDecorator(request) {
              @Override
              public Flux<DataBuffer> getBody() {
                return Flux.just(exchange.getResponse().bufferFactory().wrap(bytes));
              }
            };

            ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build();

            return chain.filter(modifiedExchange);
        })
        .flatMap(result -> result);
  }
}

Upvotes: 0

GuyT
GuyT

Reputation: 4416

The reason is that you can only read the body once (https://github.com/spring-cloud/spring-cloud-gateway/issues/1587)

You are reading it here: serverWebExchange.getRequest().getBody() and therefore it is omitted in the request.

A solution is to cache the body, you can use for instance the ReadBodyRoutePredicateFactory (https://github.com/spring-cloud/spring-cloud-gateway/issues/1307).

Make sure the RemoveCachedBodyFilter is enabled so the body is released so you do not have memory leaks.

Upvotes: 2

Related Questions