javaspringspring-bootspring-webclient

How to retry in the new HTTP interface in Spring 6 and Spring Boot 3


Spring has introduced the new HTTP interface. For exception handling the documentation states to register a response status handler that applies to all responses performed through the client:

WebClient webClient = WebClient.builder()
    .defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
    .build();

However, it's not clear how to handle retries.

In WebClient you could simple use retryWhen():

public Mono<String> getData(String stockId) {
return webClient.get()
  .uri(PATH_BY_ID, stockId)
  .retrieve()
  .bodyToMono(String.class)
  .retryWhen(Retry.backoff(3, Duration.ofSeconds(2)));
}

I'm not sure how to incorporate retries with the Http interfaces.


Solution

  • I figured it out. You need to use an exchange filter. I implemented a similiar solution for a different problem: Adding a retry all requests of WebClient

     @Bean
      TodoClient todoClient() {
        WebClient webClient =
            WebClient.builder()
                .baseUrl("sampleUrl")
                .filter(retryFilter())
                .build();
        HttpServiceProxyFactory factory =
            HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient)).build();
        return factory.createClient(TodoClient.class);
      }
    
      
    private ExchangeFilterFunction retryFilter() {
    return (request, next) ->
        next.exchange(request)
            .flatMap(clientResponse ->
                Mono.just(clientResponse)
                    .filter(response -> clientResponse.statusCode().isError())
                    .flatMap(ClientResponse::createException)
                    .flatMap(Mono::error)
                    .thenReturn(clientResponse)
            )
            .retryWhen(
                Retry.fixedDelay(3, Duration.ofSeconds(30))
                    .doAfterRetry(retrySignal -> log.warn("Retrying"))
            );
    

    }