I have been looking for a solution to the following use case without success, I hope someone can help :
Assuming the following use case. I need to call a customer Api (customerApi
) and this api needs a Bearer
token which may have expired when I call customerApi
. If the token has expired, the customerApi
returns a 401
response.
What, I want to do is to retry only once if I received a 401
and call the method to get a new Bearer
token. If the retry still returns 401
, I need to throw an Exception
The method to get a Bearer
token :
private String getToken() {
return oAuthService.getToken();
}
And the webClient
usage to call customerApi
(customerWebClient
is a bean created with WebClient.Builder
) :
public Customer getCustomerById(String customerId, String token) {
return customerWebClient.get()
.uri("myurl/customers/{customerId}, customerId)
.headers(httpHeaders -> {
httpHeaders.add(HttpHeaders.AUTHORIZATION, "Bearer " + token);
})
.retrieve()
.bodyToMono(Customer.class)
.onErrorResume(WebClientResponseException.NotFound.class, notFound ->
Mono.error(new MyCustomException()))
.block();
}
It seems that retryWhen
can only be used to upgrade timeout. So I hope that someone know how to achieve this use case ^^
Thanks for your help :)
EDIT :
I tried to use retryWhen(Retry.onlyIf(...))
from reactor-extra
but the good old retryWhen
from this package is now deprecated (solution based on : Adding a retry all requests of WebClient)
The method
public final Mono<T> retryWhen(Function<Flux<Throwable>, ? extends Publisher<?>> whenFactory)
has been deprecated and now the preferred method is
public final Mono<T> retryWhen(Retry retrySpec)
So, you can modify your code to something like this to make it work with the new retryWhen
public Customer getCustomerById(String customerId, String token) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + token);
final RetrySpec retrySpec = Retry.max(1).doBeforeRetry(
retrySignal -> headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + someTokenGetterMethod()))
.filter(throwable -> throwable.getClass() == Unauthorized.class);
return Mono.defer(() -> webClient.get().uri("myurl/customers/{customerId}, customerId")
.headers(httpHeaders -> httpHeaders.addAll(headers))
.retrieve()
.bodyToMono(Customer.class))
.retryWhen(retrySpec)
.onErrorResume(WebClientResponseException.NotFound.class,
notFound -> Mono.error(new MyCustomException()))
.block();
}
Here's a working example using https://httpbin.org/
public CommandLineRunner commandLineRunner() {
HttpHeaders headers = new HttpHeaders();
final RetrySpec retrySpec = Retry.max(1).doBeforeRetry(
retrySignal -> headers.add("Authorization", "Bearer 1234")).filter(
throwable -> throwable.getClass() == Unauthorized.class);
return args -> Mono.defer(() -> webClient.get().uri("https://httpbin.org/bearer")
.headers(httpHeaders -> httpHeaders.addAll(headers)).retrieve().toEntity(Map.class)
.retryWhen(retrySpec)
.subscribe(objectResponseEntity -> System.out
.println("objectResponseEntity = " + objectResponseEntity.getBody()));
}
Also, I do not think the way you're trying to manipulate headers in retry to add authorization token is the right way of achieving this. You must come up with a better solution/design.