【问题标题】:Spring WebClient - How to handle error scenariosSpring WebClient - 如何处理错误场景
【发布时间】:2020-06-03 21:17:06
【问题描述】:

我们将org.springframework.web.reactive.function.client.WebClientreactor.netty.http.client.HttpClient 作为 Spring 5.1.9 的一部分使用 exchange() 方法发出请求。此方法的documentation 突出显示以下内容:

... 使用 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
    }

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

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

【问题讨论】:

    标签: java spring reactor spring-webclient


    【解决方案1】:

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

    在文档中说:

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

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

    如果不需要响应内容,您也可以使用 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);
      }
    
    

    在此示例中,我使用了异常,但您可以创建一些更具体的异常,然后使用一些异常处理程序来返回您想要的 http 状态。 不建议使用阻塞,更好的方法是向前传递。

    【讨论】:

      【解决方案2】:

      创建一些异常类

      自动装配的对象映射器

      创建一个返回 Throwable 的方法

      为 Error 创建一个自定义类。

      return webClient
                      .get()
                      .uri(endpoint)
                      .retrieve()
                      .bodyToMono(Model.class)
                      .onErrorMap(WebClientException.class, this::handleHttpClientException);
      
      
      
      private Throwable handleHttpClientException(Throwable ex) {
                  if (!(ex instanceof WebClientResponseException)) {
                      LOG.warn("Got an unexpected error: {}, will rethrow it", ex.toString());
                      return ex;
                  }
          
                  WebClientResponseException wcre = (WebClientResponseException)ex;
          
                  switch (wcre.getStatusCode()) {
                      case NOT_FOUND -> throw new NotFoundException(getErrorMessage(wcre));
                      case BAD_REQUEST -> throw new BadRequestException(getErrorMessage(wcre));
                      default -> {
                          LOG.warn("Got a unexpected HTTP error: {}, will rethrow it", wcre.getStatusCode());
                          LOG.warn("Error body: {}", wcre.getResponseBodyAsString());
                          return ex;
                      }
                  }
              }
      
      
      
      private String getErrorMessage(WebClientResponseException ex) {
              try {
                  return mapper.readValue(ex.getResponseBodyAsString(), HttpErrorInfo.class).getMessage();
              } catch (IOException ioe) {
                  return ex.getMessage();
              }
          }
      

      【讨论】:

        猜你喜欢
        • 2021-08-06
        • 2019-12-04
        • 1970-01-01
        • 2021-09-25
        • 1970-01-01
        • 1970-01-01
        • 2012-02-28
        • 2020-10-31
        • 1970-01-01
        相关资源
        最近更新 更多