【问题标题】:In Spring WebClient used with block(), get body on error在与 block() 一起使用的 Spring WebClient 中,错误时获取正文
【发布时间】:2021-08-02 14:40:03
【问题描述】:

我正在使用 Spring WebFlux 中的 WebClient 与 Spring 客户端的 REST API 后端进行通信。

当此 REST API 后端引发异常时,它会以我想从我的客户端收集的特​​定格式 (ErrorDTO) 进行响应。

我试图做的是让我的客户端在服务器以 5xx HTTP 状态代码回答时抛出包含此主体的 GestionUtilisateurErrorException(ErreurDTO)。

我尝试了几种选择:

我/onStatus

@Autowired
WebClient gestionUtilisateursRestClient;

gestionUtilisateursRestClient
    .post()
    .uri(profilUri)
    .body(Mono.just(utilisateur), UtilisateurDTO.class)
    .retrieve()
    .onStatus(HttpStatus::is5xxServerError,
        response -> {
            ErreurDTO erreur = response.bodyToMono(ErreurDTO.class).block();
            
            return Mono.error(new GestionUtilisateursErrorException(erreur));
        }
    )   
    .bodyToMono(Void.class)
    .timeout(Duration.ofMillis(5000))         
    .block();

此方法不起作用,因为 webclient 不允许我调用 onStatus 中的 block 方法。我只能得到一个 Mono 对象,我不能从这里走得更远。

似乎“onStatus”方法不能在 WebClient 阻塞方法中使用,这意味着我可以抛出自定义异常,但我无法使用响应正文中的数据填充它。

II/ ExchangeFilterFunction

@Bean
WebClient gestionUtilisateursRestClient() {
    return WebClient.builder()
      .baseUrl(gestionUtilisateursApiUrl)
      .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
      .filter(ExchangeFilterFunction.ofResponseProcessor(this::gestionUtilisateursExceptionFilter))
      .build();
}   

private Mono<ClientResponse> gestionUtilisateursExceptionFilter(ClientResponse clientResponse) {
    if(clientResponse.statusCode().isError()){
        return clientResponse.bodyToMono(ErreurDTO.class)
            .flatMap(erreurDto -> Mono.error(new GestionUtilisateursErrorException(
                erreurDto
            )));
    }
    
    return Mono.just(clientResponse);
}

此方法有效,但抛出了一个我正在努力正确捕获的 reactor.core.Exceptions$ReactiveException(reactor.core.Exceptions 不可捕获,并且 ReactiveException 是私有的)。

此异常在其原因中包含我需要捕获的异常 (GestionUtilisateurErrorException),但我需要一种方法来正确捕获它。

我也尝试使用“onErrorMap”和“onErrorResume”方法,但它们都没有按我需要的方式工作。

编辑 1: 我现在正在使用以下解决方法,即使我觉得这是一种肮脏的方式来做我需要的事情:

gestionUtilisateursRestClient
            .post()
            .uri(profilUri)
            .body(Mono.just(utilisateur), UtilisateurDTO.class)
            .retrieve()
            .onStatus(h -> h.is5xxServerError(),
              response -> {
                    return response.bodyToMono(ErreurDTO.class).handle((erreur, handler) -> {                         
                      LOGGER.error(erreur.getMessage());
                      handler.error(new GestionUtilisateursErrorException(erreur));
                    });
                  }
                )
            .bodyToMono(String.class)
            .timeout(Duration.ofMillis(5000))
            
            .block();
    }
    catch(Exception e) {
        LOGGER.debug("Erreur lors de l'appel vers l'API GestionUtilisateur (...)");
        if(ExceptionUtils.getRootCause(e) instanceof GestionUtilisateursErrorException) {
            throw((GestionUtilisateursErrorException) e.getCause());
        }
        else {
            throw e;
        }
    }

在这里,它抛出了我可以同步处理的预期 GestionUtilisateursErrorException。

我可能会在全局处理程序中实现这一点,以避免在每次调用我的 API 时编写此代码。

谢谢。 凯文

【问题讨论】:

    标签: rest exception spring-webclient


    【解决方案1】:

    我遇到过使用Mono.handle() 方法访问可能对您有用的响应正文的类似案例(请参阅https://projectreactor.io/docs/core/release/api/index.html?reactor/core/publisher/Mono.html)。

    这里的handlerSynchronousSink(参见https://projectreactor.io/docs/core/release/api/reactor/core/publisher/SynchronousSink.html),最多可以调用一次next(T),并且可以调用complete()error()

    在这种情况下,我调用 'handler.error()' 并使用由 'erreur' 构造的新 GestionUtilisateursErrorException。

    .onStatus(h -> h.is5xxServerError(),
      response -> {
        return response.bodyToMono(ErreurDTO.class).handle((erreur, handler) -> {
          // Do something with erreur e.g.
          log.error(erreur.getErrorMessage());
          // Call handler.next() and either handler.error() or handler.complete()
          handler.error(new GestionUtilisateursErrorException(erreur));
        });
      }
    )
    

    【讨论】:

    • 我试图实现这个解决方案。由于“log.error”操作,我实际上可以记录服务器响应的内容。但它仍然会抛出 reactor.core.Exceptions$ReactiveException。我找到了一个解决方法,我指定为编辑或我的原始问题,但我觉得这是一种肮脏的方式。
    猜你喜欢
    • 2021-08-02
    • 2021-04-12
    • 2021-12-03
    • 1970-01-01
    • 2019-09-08
    • 2021-07-04
    • 1970-01-01
    • 2020-12-21
    • 2013-02-27
    相关资源
    最近更新 更多