简体   繁体   中英

"IllegalStateException: Only one connection receive subscriber allowed" when response body is empty

I have a Spring Boot 2.3.1 project, in which I use WebClient to call a remote service.

The remote service is not very reliable and tends to return 500 errors, with and without response bodies. My goal is throw a custom exception that contains the response body (or a default message) so that I can log it, and here's my code :

      webClient.get()
          .uri(targetServiceUri)
          .retrieve()
          .onStatus(HttpStatus::is5xxServerError, clientResponse ->
              clientResponse.bodyToMono(String.class)
                  .flatMap(error ->
                      Mono.error(new MyCustomServiceException(error))
                  )
          )
          .toEntity(String.class)
          .block();

I have 2 tests using wiremock, the first one works :

  @Test
  void shouldThrowCustomExceptionWhenServiceReturns500ServerErrorWithNoBody() {
    setStubForInValidCheckCall(HttpStatus.INTERNAL_SERVER_ERROR,"{'Error':'invalid request'}");

    Throwable thrown =
        catchThrowable(() -> myClient.performComplianceCheck(getCompany()));

    assertThat(thrown)
        .isInstanceOf(MyCustomServiceException.class)
        .hasMessageContaining("{'Error':'invalid request'}");
  }
  
   private void setStubForInValidCheckCall(HttpStatus httpStatus, String body) {

    var response= aResponse().withStatus(httpStatus.value());

    if(body!=null){
      response=response.withBody(body);
    }

    stubFor(
        get(urlPathMatching("/targetCompliance"))
            .willReturn(response));
  }

However, the second test in which the response is 500 but there's no body (or if it's an empty string), fails with "java.lang.IllegalStateException: Only one connection receive subscriber allowed.

  @Test
  void shouldThrowCustomExceptionWhenServiceReturns500ServerErrorWithNoBody() {
    setStubForInValidCheckCall(HttpStatus.INTERNAL_SERVER_ERROR,null);

    Throwable thrown =
        catchThrowable(() -> myClient.performComplianceCheck(getCompany()));

    assertThat(thrown)
        .isInstanceOf(MyCustomServiceException.class)
        .hasMessageContaining("service returned status 500");
  }

I am struggling to understand why this happens, and how to fix it..

is it "normal" ? or am I missing something obvious (is it a problem with my test ?) ?

I have found a workaround, but it doesn't feel "webFlux-y" at all, and I still don't understand why the Only one connection receive subscriber allowed was happening :

try {   
 ResponseEntity<String> responseEntity =
      webClient.get()
          .uri(targetServiceUri)
          .retrieve()
          .toEntity(String.class)
          .block();
}
catch (WebClientException e) {

  if(e instanceof InternalServerError){
    var internalServerError=(InternalServerError) e;

    if(internalServerError.getStatusCode().is5xxServerError()){

      var respBody=internalServerError.getResponseBodyAsString();

      if(StringUtils.isEmpty(respBody)){
        respBody=MY_STANDARD_MESSAGE +internalServerError.getRawStatusCode() ;
      }

      throw new MyCustomServiceException(respBody);

    }

  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM