Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring WebClient - How to handle error scenarios

We're using org.springframework.web.reactive.function.client.WebClient with reactor.netty.http.client.HttpClient as part of Spring 5.1.9 to make requests using the exchange() method. The documentation for this method highlights the following:

... when using exchange(), it is the responsibility of the application to consume any response content regardless of the scenario (success, error, unexpected data, etc). Not doing so can cause a memory leak.

Our use of exchange() is rather basic, but the documentation for error scenarios is unclear to me and I want to be certain that we are correctly releasing resources for all outcomes. In essence, we have a blocking implementation which makes a request and returns the ResponseEntity regardless of the response code:

    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
    }

If I understand the implementation, exchange() will always give us a response if the request was successfully dispatched, regardless of response code (e.g. 4xx, 5xx). In that scenario, we just need to invoke toEntity() to consume the response. My concern is for error scenarios (e.g. no response, low-level connection errors, etc). Will the above exception handling catch all other scenarios and will any of them have a response that needs to be consumed?

Note: ClientResponse.releaseBody() was only introduced in 5.2

like image 363
user1491636 Avatar asked Feb 19 '20 16:02

user1491636


People also ask

How does WebClient handle error response?

While Initialising WebClient As mentioned in the code block, whenever a 5XX/4XX Error occurs, we can throw a user defined exception, and then execute error handling logic based on those user defined exceptions. Once this error Handler is defined, we can add it in the WebClient Initialisation.

How do you handle errors in spring?

Spring MVC Framework provides following ways to help us achieving robust exception handling. Controller Based - We can define exception handler methods in our controller classes. All we need is to annotate these methods with @ExceptionHandler annotation. This annotation takes Exception class as argument.

How does Spring Batch handle errors?

Spring Batch has built-in support, its skip and retry features, to handle errors when a job is executing. Skip and retry are about avoiding crashes, but crashes are inevitable, so Spring Batch also supports restarting a job after a failed execution.

How does WebFlux handle exceptions in spring?

There are three ways that we can use onErrorResume to handle errors: Compute a dynamic fallback value. Execute an alternative path with a fallback method. Catch, wrap and re-throw an error, e.g., as a custom business exception.

What is the error handling for retrieve () method of spring webclient?

When using the retrieve () method of Spring WebClient in conjunction with bodyToMono default error handling is applied (if the response has status code 4xx or 5xx, the Mono will contain a WebClientException).

How to handle errors in WebClient?

Once you start to use it, you will have various methods to put in use for sure. To handle errors in WebClient, you can use extend retrieve method. The retrieve method in WebClient throws an WebClientResponseException when there will be a 4xx and 5xx series exception received. You can further customize it using the onStatus () method, like below.

How to handle exceptions in Spring Boot with webclient?

We can use onStatus (Predicate<HttpStatus> statusPredicate, Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction) method to handle or customize the exception. We recommend, go through the Spring Boot– Consuming a REST Services with WebClient article before proceeding. 1.

How to handle webclientresponseexception 4xx or 5xx in Java?

The retrieve () method in WebClient throws a WebClientResponseException whenever the API response with status code 4xx or 5xx is received. We can use onStatus (Predicate<HttpStatus> statusPredicate, Function<ClientResponse, Mono<? extends Throwable>> exceptionFunction) method to handle or customize the exception.


Video Answer


1 Answers

The response have to be consumed when the request was made, but if you can't do the request probably an exception was be throwed before, and you will no have problems with response.

In the documentation says:

NOTE: When using a ClientResponse through the WebClient exchange() method, you have to make sure that the body is consumed or released by using one of the following methods:

  1. body(BodyExtractor)
  2. bodyToMono(Class) or bodyToMono(ParameterizedTypeReference)
  3. bodyToFlux(Class) or bodyToFlux(ParameterizedTypeReference)
  4. toEntity(Class) or toEntity(ParameterizedTypeReference)
  5. toEntityList(Class) or toEntityList(ParameterizedTypeReference)
  6. toBodilessEntity()
  7. releaseBody()

You can also use bodyToMono(Void.class) if no response content is expected. However keep in mind the connection will be closed, instead of being placed back in the pool, if any content does arrive. This is in contrast to releaseBody() which does consume the full body and releases any content received.

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

You can try to use .retrieve() instead .exchange() and handle errors as your preference.

  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);
  }

In this example I used Exception but you can create some exception more specific and then use some exception handler to return the http status that you want. Is not recommended to use block, a better way is pass the stream forward.

like image 130
Raul Lopes Avatar answered Oct 18 '22 19:10

Raul Lopes