Spring WebClient:重试中的调用方法
Spring WebClient : Call method in retry
我一直在寻找以下用例的解决方案,但没有成功,希望有人能提供帮助:
假设以下用例。我需要打电话给客户 Api (customerApi
),这个 api 需要一个 Bearer
令牌,当我打电话给 customerApi
时它可能已经过期了。如果令牌已过期,customerApi
returns 401
响应。
什么,我想做的是仅在收到 401
时重试一次并调用该方法来获取新的 Bearer
令牌。如果重试还是returns401
,我需要抛出一个Exception
获得Bearer
令牌的方法:
private String getToken() {
return oAuthService.getToken();
}
以及调用 customerApi
的 webClient
用法(customerWebClient
是使用 WebClient.Builder
创建的 bean):
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();
}
看来retryWhen
只能用来升级超时了。所以我希望有人知道如何实现这个用例^^
感谢您的帮助:)
编辑:
我尝试使用 reactor-extra
中的 retryWhen(Retry.onlyIf(...))
,但是这个包中的旧 retryWhen
现在已被弃用(解决方案基于:Adding a retry all requests of WebClient)
方法
public final Mono<T> retryWhen(Function<Flux<Throwable>, ? extends Publisher<?>> whenFactory)
已弃用,现在首选方法是
public final Mono<T> retryWhen(Retry retrySpec)
因此,您可以将代码修改为类似这样的内容,以使其适用于新的 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();
}
这是一个使用 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()));
}
此外,我认为您在重试添加授权令牌时尝试操纵 headers 的方式不是实现此目的的正确方法。你一定想出更好的solution/design.
我一直在寻找以下用例的解决方案,但没有成功,希望有人能提供帮助:
假设以下用例。我需要打电话给客户 Api (customerApi
),这个 api 需要一个 Bearer
令牌,当我打电话给 customerApi
时它可能已经过期了。如果令牌已过期,customerApi
returns 401
响应。
什么,我想做的是仅在收到 401
时重试一次并调用该方法来获取新的 Bearer
令牌。如果重试还是returns401
,我需要抛出一个Exception
获得Bearer
令牌的方法:
private String getToken() {
return oAuthService.getToken();
}
以及调用 customerApi
的 webClient
用法(customerWebClient
是使用 WebClient.Builder
创建的 bean):
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();
}
看来retryWhen
只能用来升级超时了。所以我希望有人知道如何实现这个用例^^
感谢您的帮助:)
编辑:
我尝试使用 reactor-extra
中的 retryWhen(Retry.onlyIf(...))
,但是这个包中的旧 retryWhen
现在已被弃用(解决方案基于:Adding a retry all requests of WebClient)
方法
public final Mono<T> retryWhen(Function<Flux<Throwable>, ? extends Publisher<?>> whenFactory)
已弃用,现在首选方法是
public final Mono<T> retryWhen(Retry retrySpec)
因此,您可以将代码修改为类似这样的内容,以使其适用于新的 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();
}
这是一个使用 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()));
}
此外,我认为您在重试添加授权令牌时尝试操纵 headers 的方式不是实现此目的的正确方法。你一定想出更好的solution/design.