Spring WebClient:重试调用方法

问题描述

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

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

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

获取Bearer令牌的方法

private String getToken() {
    return oAuthService.getToken();
}

然后使用webClient调用customerApicustomerWebClient是使用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仅可用于升级超时。所以我希望有人知道如何实现此用例^^

感谢您的帮助:)

编辑:

我尝试使用retrywhen(Retry.onlyIf(...))中的reactor-extra,但此软件包中的旧版本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()));
}

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