从ServerHttpRequest / Flux<DataBuffer>获取请求正文字符串

11

我正在使用Spring Boot版本-2.0.6.RELEASE和Spring Cloud版本-Finchley.SR2,

并且我创建了自定义网关过滤器来修改请求正文。

但是在使用Flux将请求正文转换为字符串时,我得到了一个空字符串。我需要一种方法来获取与我的请求正文相对应的字符串。

@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();

}
5个回答

9
您可以使用ModifyRequestBodyGatewayFilterFactory,我相信它已经包含在Spring Cloud Gateway 2.0.2中,这是Finchley的一部分。
例如:
@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);
    };
}

5

这是 Spring Cloud Gateway 2.2.5 中另一种处理方式,我们将使用 ReadBodyPredicateFactory。这样做可以将 requestBody 缓存到带有属性键 cachedRequestBodyObject 的 ServerWebExchange 中。

创建一个始终为真的 Predicate。

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


在application.yml中,添加Predicate。
spring:
  cloud:
    gateway:
      routes:
       ....
          predicates:
            .....
            - name: ReadBodyPredicateFactory
              args:
                inClass: "#{T(String)}" 
                predicate: "#{@testRequestBody}"

在您自己的过滤器中,可以通过以下方式获取requestBody:
    @Override
    public GatewayFilter apply(Object config)
    {
        return (exchange, chain) -> {

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

        };
    }

3
一旦您通过阅读(日志记录)请求体,请求就会在那里停止。Spring Cloud Gateway 需要记录请求体的内容,但请求体只能被读取一次。如果在读取后不对请求体进行封装,则后续服务将无法读取请求体数据。点击此处以了解更多信息。

3

优化 @tony.hokan 的答案 https://dev59.com/s1MH5IYBdhLWcg3w7VlM#64080867,使用 spring cloud gateway 重写请求体以将请求体(可能还包括响应体)保存为 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();
    }

在您自己的过滤器中,获取请求体如下所示:
    @Override
    public GatewayFilter apply(Object config)
    {
        return (exchange, chain) -> {

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

        };
    }

你为我节省了很多时间和研究,非常感谢。你是个英雄。 - riccardo.cardin

0

来自Spring Cloud Gateway官方文档

路由配置中,在您的自定义配置之前添加Spring CacheRequestBody

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

或者如果你正在使用.properties而不是.yaml

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

现在,在 Spring 的每个过滤器链中,您可以通过调用 exchange.getAttribute(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR); 来获取请求体的缓存副本:

CacheRequestBody 过滤器之后的每个过滤器都可以这样做。

@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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接