Spring WebClient - 如何处理错误场景

use*_*636 5 java spring reactor spring-webclient

我们使用org.springframework.web.reactive.function.client.WebClientwith reactor.netty.http.client.HttpClient作为 Spring 5.1.9 的一部分来使用该exchange()方法发出请求。此方法的文档强调了以下几点:

...在使用 exchange() 时,无论场景如何(成功、错误、意外数据等),应用程序都有责任使用任何响应内容。不这样做会导致内存泄漏。

我们对 的使用exchange()相当基本,但我不清楚错误场景的文档,我想确定我们是否正确地为所有结果释放了资源。本质上,我们有一个阻塞实现,它发出请求并返回ResponseEntity无论响应代码如何:

    try {
        ...
        ClientResponse resp = client.method(method).uri(uri).syncBody(body).exchange().block();
        ResponseEntity<String> entity =  resp.toEntity(String.class).block();
        return entity;
    } catch (Exception e) {
        // log error details, return internal server error
    }
Run Code Online (Sandbox Code Playgroud)

如果我理解实现,exchange()无论响应代码如何(例如 4xx、5xx),如果请求成功发送,将始终给我们一个响应。在这种情况下,我们只需要调用toEntity()即可使用响应。我担心的是错误情况(例如,无响应、低级连接错误等)。上述异常处理是否会捕获所有其他场景,并且它们中的任何一个都有需要消耗的响应?

注意:ClientResponse.releaseBody()仅在 5.2 中引入

小智 7

在发出请求时必须消耗响应,但是如果您不能执行请求,则可能之前抛出了异常,并且响应不会出现问题。

在文档中说:

注意:通过 WebClient exchange() 方法使用 ClientResponse 时,您必须确保使用以下方法之一消耗或释放主体:

  1. 身体(身体提取器)
  2. bodyToMono(Class) 或 bodyToMono(ParameterizedTypeReference)
  3. bodyToFlux(Class) 或 bodyToFlux(ParameterizedTypeReference)
  4. toEntity (Class) 或 toEntity(ParameterizedTypeReference)
  5. toEntityList(Class) 或 toEntityList(ParameterizedTypeReference)
  6. toBodilessEntity()
  7. 释放体()

如果不需要响应内容,您也可以使用 bodyToMono(Void.class)。但是请记住,如果任何内容确实到达,连接将被关闭,而不是被放回池中。这与 releaseBody() 形成对比,后者确实消耗整个身体并释放收到的任何内容。

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/function/client/ClientResponse.html

您可以尝试使用 .retrieve() 代替 .exchange() 并根据您的喜好处理错误。

  public Mono<String> someMethod() {

    return webClient.method(method)
      .uri(uri)
      .retrieve()
      .onStatus(
        (HttpStatus::isError), // or the code that you want
        (it -> handleError(it.statusCode().getReasonPhrase())) //handling error request
      )
      .bodyToMono(String.class);


  }

  private Mono<? extends Throwable> handleError(String message) {
    log.error(message);
    return Mono.error(Exception::new);
  }

Run Code Online (Sandbox Code Playgroud)

在这个例子中,我使用了 Exception 但你可以创建一些更具体的异常,然后使用一些异常处理程序来返回你想要的 http 状态。 不推荐使用block,更好的方法是向前传递流。