Spring WebClient:重试中调用方法

Pie*_*nes 3 reactor-netty spring-webflux spring-webclient

我一直在寻找以下用例的解决方案但没有成功,我希望有人可以提供帮助:

假设以下用例。我需要调用客户 Api( customerApi),而该 api 需要一个Bearer令牌,当我调用时该令牌可能已过期customerApi。如果令牌已过期,则customerApi返回401响应。

我想做的是,如果我收到一个401并调用该方法来获取新Bearer令牌,则仅重试一次。如果重试仍然返回401,我需要抛出一个Exception

获取token的方法Bearer

private String getToken() {
    return oAuthService.getToken();
}
Run Code Online (Sandbox Code Playgroud)

webClient调用customerApi(customerWebClient是用 ) 创建的 bean 的用法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();
    }
Run Code Online (Sandbox Code Playgroud)

看来retryWhen只能用超时来升级了。所以我希望有人知道如何实现这个用例^^

感谢您的帮助 :)

编辑 :

我尝试使用retryWhen(Retry.onlyIf(...))fromreactor-extra但此包中的旧版本retryWhen现已弃用(解决方案基于:添加重试 WebClient 的所有请求

Isa*_*ank 6

方法

public final Mono<T> retryWhen(Function<Flux<Throwable>, ? extends Publisher<?>> whenFactory)
Run Code Online (Sandbox Code Playgroud)

已被弃用,现在首选方法是

public final Mono<T> retryWhen(Retry retrySpec)
Run Code Online (Sandbox Code Playgroud)

因此,您可以将代码修改为类似的内容,以使其与新版本一起使用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();
}
Run Code Online (Sandbox Code Playgroud)

这是一个使用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()));
}
Run Code Online (Sandbox Code Playgroud)

另外,我不认为您尝试在重试添加授权令牌时操作标头的方式是实现此目的的正确方法。您必须想出更好的解决方案/设计。