Get request body string from ServerHttpRequest / Flux<DataBuffer>
Asked Answered
S

5

11

I am using spring boot version - 2.0.6.RELEASE and spring cloud version - Finchley.SR2

and i have created my custom gateway filter to modify the request body.

but while converting the request body to string using Flux i am getting a empty string. i need a method to get the string corresponding to my request body.

@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    ServerHttpRequest request = (ServerHttpRequest) exchange.getRequest();
    String s = resolveBodyFromRequest(request);
     /* s comes out to be "" */
    return chain.filter(newExchange);


}



private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest){
    //Get the request body
    Flux<DataBuffer> body = serverHttpRequest.getBody();
    StringBuilder sb = new StringBuilder();

    body.subscribe(buffer -> {
        byte[] bytes = new byte[buffer.readableByteCount()];
        buffer.read(bytes);
        DataBufferUtils.release(buffer);
        String bodyString = new String(bytes, StandardCharsets.UTF_8);
        sb.append(bodyString);
    });
    return sb.toString();

}
Sidras answered 19/8, 2019 at 19:30 Comment(0)
C
9

You could use the ModifyRequestBodyGatewayFilterFactory which I believe is included in Spring Cloud Gateway 2.0.2 which is part of Finchley.

For Ex:

@Override
public GatewayFilter apply(Config config) {
   return (exchange, chain) -> {
        ModifyRequestBodyGatewayFilterFactory.Config modifyRequestConfig = new ModifyRequestBodyGatewayFilterFactory.Config()
                .setContentType(ContentType.APPLICATION_JSON.getMimeType())
                .setRewriteFunction(String.class, String.class, (exchange1, originalRequestBody) -> {
                    String modifiedRequestBody = yourMethodToModifyRequestBody(originalRequestBody);
                    return Mono.just(modifiedRequestBody);
                });

        return new ModifyRequestBodyGatewayFilterFactory().apply(modifyRequestConfig).filter(exchange, chain);
    };
}
Croon answered 19/2, 2020 at 22:13 Comment(0)
A
5

This is another approach work in spring cloud gateway 2.2.5, we will use ReadBodyPredicateFactory, as this will cache requestBody to ServerWebExchange with attribute key cachedRequestBodyObject

create always true Predicate

@Component
public class TestRequestBody implements Predicate
{
    @Override
    public boolean test(Object o)
    {
        return true;
    }
}

in application.yml, add Predicate

spring:
  cloud:
    gateway:
      routes:
       ....
          predicates:
            .....
            - name: ReadBodyPredicateFactory
              args:
                inClass: "#{T(String)}" 
                predicate: "#{@testRequestBody}"

in your own filter, get requestBody like below:

    @Override
    public GatewayFilter apply(Object config)
    {
        return (exchange, chain) -> {

            String requestBody = exchange.getAttribute("cachedRequestBodyObject");

        };
    }
Acantho answered 26/9, 2020 at 17:51 Comment(0)
L
3

once you read(log by reading) the request body, request drops there itself. spring cloud gateway needs to record the contents of the request body, but the request body can only be read once. If the request body is not encapsulated after reading it , the latter service will not be able to read the body data. follow this

Langrage answered 26/8, 2019 at 4:58 Comment(0)
O
3

Refinement of @tony.hokan answer https://mcmap.net/q/324403/-get-request-body-string-from-serverhttprequest-flux-lt-databuffer-gt using spring cloud gateway rewrite body request to save the request body (and possibly response body) as an attribute of the org.springframework.web.server.ServerWebExchange

    @Bean
    public RouteLocator myRouteSavingRequestBody(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("my-route-id",
                p -> p
                    .path("/v2/**") //your own path filter
                    .filters(f -> f
                        .modifyResponseBody(String.class, String.class,
                            (webExchange, originalBody) -> {
                                if (originalBody != null) {
                                    webExchange.getAttributes().put("cachedResponseBodyObject", originalBody);
                                    return Mono.just(originalBody);
                                } else {
                                    return Mono.empty();
                                }
                            })
                        .modifyRequestBody(String.class, String.class,
                            (webExchange, originalBody) -> {
                                if (originalBody != null) {
                                    webExchange.getAttributes().put("cachedRequestBodyObject", originalBody);
                                    return Mono.just(originalBody);
                                } else {
                                    return Mono.empty();
                                }
                            })

                    )
                    .uri("https://myuri.org")
            )
            .build();
    }

in your own filter, get requestBody like below:

    @Override
    public GatewayFilter apply(Object config)
    {
        return (exchange, chain) -> {

            String requestBody = exchange.getAttribute("cachedRequestBodyObject");

        };
    }
Overflow answered 26/10, 2020 at 10:21 Comment(1)
You saved me a lot of time and research. Thanks a lot. You're a hero.Disentangle
W
0

From Spring Cloud Gateway official documentation:

in the routes configuration add Spring CacheRequestBody before your custom one:

spring:
  cloud:
    gateway:
      routes:
      - id: cache_request_body_route
        uri: lb://downstream
        predicates:
        - Path=/downstream/**
        filters:
        - name: CacheRequestBody
          args:
            bodyClass: java.util.LinkedHashMap

or if you are using .properties instead .yaml:

spring.cloud.gateway.routes[77].filters[2]=CacheRequestBody=java.util.LinkedHashMap
spring.cloud.gateway.routes[77].filters[3]=YourCustomFilter

now in every filter in the chain after Spring CacheRequestBody you can get a cached copy of the request body simply calling exchange.getAttribute(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR);:

@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    LinkedHashMap<String,Object> body = exchange.getAttribute(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR);
    //do-stuff-with-body
    return chain.filter(exchange);
}
Wit answered 20/1, 2023 at 9:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.