【问题标题】:How to extract response header & status code from Spring 5 WebClient ClientResponse如何从 Spring 5 WebClient ClientResponse 中提取响应头和状态码
【发布时间】:2018-10-17 19:57:32
【问题描述】:

我是 Spring Reactive 框架的新手,正在尝试将 Springboot 1.5.x 代码转换为 Springboot 2.0。我需要在 Spring 5 WebClient ClientResponse 的一些过滤、正文和状态代码之后返回响应标头。我不想使用 block() 方法,因为它会将其转换为同步调用。 我可以很容易地使用 bodyToMono 获得 responsebody。此外,如果我只是返回 ClientResponse,我会获取状态代码、标题和正文,但我需要根据 statusCode 和标题参数处理响应。 我尝试了订阅、flatMap 等,但没有任何效果。

例如- 下面的代码将返回响应正文

Mono<String> responseBody =  response.flatMap(resp -> resp.bodyToMono(String.class));

但类似的范例无法获取 statusCode 和 Response 标头。 有人可以帮助我使用 Spring 5 反应框架提取 statusCode 和标头参数。

【问题讨论】:

    标签: java spring spring-boot spring-webflux


    【解决方案1】:

    您可以使用webclient的交换功能,例如

    Mono<String> reponse = webclient.get()
    .uri("https://stackoverflow.com")
    .exchange()
    .doOnSuccess(clientResponse -> System.out.println("clientResponse.headers() = " + clientResponse.headers()))
    .doOnSuccess(clientResponse -> System.out.println("clientResponse.statusCode() = " + clientResponse.statusCode()))
    .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
    

    然后你可以转换 bodyToMono 等

    【讨论】:

    • 但这只是打印 HttpStatus 代码。如果我需要返回它的值怎么办?这可能吗?
    • 这应该被标记为接受的答案!它对我有用,谢谢!
    • @C96 这些是异步调用,因此您无法返回传统意义上的值。您应该只能返回 MonoFlux。在doOnSuccess 方法内部进行处理。
    • @thisishantzz 你能给我举个例子吗?
    【解决方案2】:

    我还需要检查响应详细信息(标题、状态等)和正文。

    我能够做到这一点的唯一方法是使用.exchange() 和两个subscribe(),如下例所示:

        Mono<ClientResponse> clientResponse = WebClient.builder().build()
                .get().uri("https://stackoverflow.com")
                .exchange();
    
        clientResponse.subscribe((response) -> {
    
            // here you can access headers and status code
            Headers headers = response.headers();
            HttpStatus stausCode = response.statusCode();
    
            Mono<String> bodyToMono = response.bodyToMono(String.class);
            // the second subscribe to access the body
            bodyToMono.subscribe((body) -> {
    
                // here you can access the body
                System.out.println("body:" + body);
    
                // and you can also access headers and status code if you need
                System.out.println("headers:" + headers.asHttpHeaders());
                System.out.println("stausCode:" + stausCode);
    
            }, (ex) -> {
                // handle error
            });
        }, (ex) -> {
            // handle network error
        });
    

    我希望它有所帮助。 如果有人知道更好的方法,请告诉我们。

    【讨论】:

    • 如何从这个 subscribe() -> {} 中读取状态码?比如,如果我需要将状态码传递给另一个方法
    【解决方案3】:

    对于状态码,你可以试试这个:

    Mono<HttpStatus> status = webClient.get()
                    .uri("/example")
                    .exchange()
                    .map(response -> response.statusCode());
    

    对于标题:

    Mono<HttpHeaders> result = webClient.get()
                    .uri("/example")
                    .exchange()
                    .map(response -> response.headers().asHttpHeaders());
    

    【讨论】:

    • 如何打印“状态”值?就像“200”而不是整个 Mono 对象
    【解决方案4】:

    在 Spring Boot 2.4.x / Spring 5.3 之后,WebClient exchange 方法被弃用,取而代之的是 retrieve,因此您必须使用 ResponseEntity 获取标头和响应状态,如下例所示:

    webClient
            .method(HttpMethod.POST)
            .uri(uriBuilder -> uriBuilder.path(loginUrl).build())
            .bodyValue(new LoginBO(user, passwd))
            .retrieve()
            .toEntity(LoginResponse.class)
            .filter(
                entity ->
                    entity.getStatusCode().is2xxSuccessful()
                        && entity.getBody() != null
                        && entity.getBody().isLogin())
            .flatMap(entity -> Mono.justOrEmpty(entity.getHeaders().getFirst(tokenHeader)));
    

    【讨论】:

      【解决方案5】:

      如果你使用WebClient,你可以配置spring boot >= 2.1.0来记录请求和响应:

      spring.http.log-request-details: true
      logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions: TRACE
      

      in the sprint boot docs 所述,如果您也希望记录标头,则必须添加

      Consumer<ClientCodecConfigurer> consumer = configurer ->
          configurer.defaultCodecs().enableLoggingRequestDetails(true);
      
      WebClient webClient = WebClient.builder()
          .exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).build())
          .build();
      

      但请注意,这可能会记录敏感信息。

      【讨论】:

      • 提问者说...我需要根据 statusCode & header 参数来处理响应。。但是您提供的代码用于日志记录配置,这意味着它在问题的上下文中没有帮助。因此-1。
      • @AdinduStevens,很抱歉我没有从问题中得到答案。对于有人登陆这里并且只想记录状态编码器和标头参数的情况,我将在此处留下答案。
      【解决方案6】:

      如上所述,交换已被弃用,因此我们使用retrieve()。这就是我在发出请求后返回代码的方式。

      public HttpStatus getResult() {
          WebClient.ResponseSpec response = client
                  .get()
                  .uri("/hello")
                  .accept(MediaType.APPLICATION_JSON)
                  .retrieve();
      
          return Optional.of(response.toBodilessEntity().block().getStatusCode()).get();
      }
      

      【讨论】:

        【解决方案7】:
         httpClient
                    .get()
                    .uri(url)
                    .retrieve()
                    .toBodilessEntity()
                    .map(reponse -> Tuple2(reponse.statusCode, reponse.headers))
        

        【讨论】:

          猜你喜欢
          • 2021-12-14
          • 2019-04-11
          • 1970-01-01
          • 1970-01-01
          • 2020-10-30
          • 1970-01-01
          • 2021-11-15
          • 2021-08-28
          • 2018-03-27
          相关资源
          最近更新 更多