Spring WebClient:重试调用方法

3

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

假设以下用例。我需要调用客户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只能用于升级超时。所以我希望有人知道如何实现这个用例^^
谢谢你的帮助 :)
编辑:
我尝试使用reactor-extra中的retryWhen(Retry.onlyIf(...)),但是这个包中的老旧的retryWhen现在已经被弃用了(基于Adding a retry all requests of WebClient的解决方案)。

我在任何retryWhen的https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#retryWhen-reactor.util.retry.Retry-上没有看到任何弃用通知。 - undefined
1个回答

6
该方法。
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()));
}

此外,我认为你试图通过操作标头来重试添加授权令牌的方式并不是实现这一目的的正确方法。你必须提出更好的解决方案/设计。


1
完美的解决方案!非常感谢,由于这不是正确的方法,我简化了主题与我需要实现的真实用例相比。 - undefined
确实是个很好的解决方案,非常感谢 @Isank - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接