将 ClientResponse 转换为 ServerResponse

Convert ClientResponse to ServerResponse

我正在尝试在 REST 服务器和调用客户端应用程序之间编写一种代理,以便通过向 REST 调用添加属性来加强隐私。

我不想检查服务器的响应,我只想将其传递给客户端。

我想用 spring-webflux 来做这个,因为我希望用事件驱动的方法节省一些 CPU。但我完全卡住了。

这是我的尝试:

public Mono<ServerResponse> select(ServerRequest request) {
  return request.principal().flatMap((principal) -> {
    return WebClient.create(solrUrl).get().uri(f -> {
              URI u = f.path(request.pathVariable("a")).path("/b/").queryParams(queryModifier.modify(principal, request.pathVariable("collection"), request.queryParams()).block()).build();
              if (debug) {
                log.debug("Calling {}", u);
              }
              return u;
            })
        .exchange()
          .flatMap((ClientResponse mapper) -> {
            BodyBuilder bodyBuilder = ServerResponse.status(mapper.statusCode());
            bodyBuilder.body(BodyInserters.fromDataBuffers(mapper.bodyToFlux(DataBuffer.class)));
            bodyBuilder.headers(c -> mapper.headers().asHttpHeaders().forEach((name, value) -> c.put(name, value)));
              //.body(DefaultserverreBodyInserters.fromPublisher(mapper.bodyToMono(DataBuffer.class), DataBuffer.class)));
              //.body(BodyInserters.fromDataBuffers(mapper.bodyToFlux(DataBuffer.class))));
              //.body(BodyInserters.fromDataBuffers(mapper.body(BodyExtractors.toDataBuffers()))));
              //.body(mapper.bodyToMono(String.class), String.class));
              //.build());
            return bodyBuilder.build();
          });
  });
}

我通过 RouterFunction 将其绑定到面向 REST API 的客户端:

@Configuration
public class VoiceRouterFunctions {

  @Bean
  public RouterFunction<ServerResponse> route(ClientPropertiesRequestHandler handler) {
    return RouterFunctions.route(RequestPredicates.GET("/v3/{a}/select")
        .and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), handler::select);
  }
}

响应为 200 - OK,正文中没有任何内容。有什么想法吗?

出于某种原因我无法理解,这有效:

public Mono<ServerResponse> select(ServerRequest request) {
  return request.principal().flatMap((principal) -> {
    return client.get().uri(f -> {
              URI u = f.path(request.pathVariable("collection")).path("/select/").queryParams(queryModifier.modify(principal, request.pathVariable("collection"), request.queryParams()).block()).build();
              if (debug) {
                log.debug("Calling {}", u);
              }
              return u;
            })
        .exchange()
          .flatMap((ClientResponse mapper) -> {
            return ServerResponse.status(mapper.statusCode())
              .headers(c -> mapper.headers().asHttpHeaders().forEach((name, value) -> c.put(name, value)))
              .body(mapper.bodyToFlux(DataBuffer.class), DataBuffer.class);
          });
  });
}

我已经尝试了十几个或更多的变体,之前没有任何效果,但现在这个...任何人都可以解释,为什么,为什么必须特别是这个?抱歉,但是 API 不适合调试,我不得不退回到试错法,这让我很紧张...

以下是对你的问题的解释,希望能帮助你理解为什么会出现这种情况。

您问题中的错误如下:BodyBuilder.body(...) 方法实际上 buildsreturns Mono<ServerResponse>,但它 不会 修改生成器。如果您查看 source code of the builder.

就可以看到这一点

看你的代码更容易理解:

BodyBuilder bodyBuilder = ServerResponse.status(...);  // 1
bodyBuilder.body(...);                                 // 2
bodyBuilder.headers(...);                              // 3
return bodyBuilder.build();                            // 4

事情是这样的:在第 1 行你创建了一个构建器,在第 2 行你添加了一个 body 并构建它,但你没有将结果存储在变量中(糟糕!),然后在第 3 行,您将 headers 添加到初始构建器,它没有 body,因为 body(...) 函数不会修改构建器,在第 4 行,您构建并 return 空的建造者。所以,在你的情况下,第 2 行什么都不做。

解决方案,正如你在回答中偶然发现的那样,是在最后调用body(...)函数,它应该是最后一个!

下面的例子也可以。注意行的新顺序。

BodyBuilder bodyBuilder = ServerResponse.status(...);  // 1
bodyBuilder.headers(...);                              // 3
return bodyBuilder.body(...);                          // 2

我编写了以下方法将 ClientResponse 转换为 ServerResponse:

private static Mono<ServerResponse> fromClientResponse(ClientResponse clientResponse){
    return ServerResponse.status(clientResponse.statusCode())
                         .headers(headerConsumer -> clientResponse.headers().asHttpHeaders().forEach(headerConsumer::addAll))
                         .body(clientResponse.bodyToMono(String.class), String.class);
}

纯转换,本意是每次不加载body到内存,如下所示:

Mono<ServerResponse> monoResponse = 
  webClient
   .method(method)
   .uri(uri)
   .headers(headers)
   .body(someBodyInserter)

   // pass-through
   .retrieve()
   .onRawStatus(status -> true, response -> Mono.empty())
   .toEntityFlux(DataBuffer.class)
   .flatMap(response -> ServerResponse
     .status(response.getStatusCode())
     .headers(respHeaders -> filterResponseHeaders(response.getHeaders(), respHeaders))
     .body(BodyInserters.fromDataBuffers(response.getBody())));