【问题标题】:Spring WebClient : Call method in retrySpring WebClient:重试调用方法
【发布时间】:2021-01-28 23:09:17
【问题描述】:

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

假设以下用例。我需要给客户 Api (customerApi) 打电话,这个 api 需要一个Bearer 令牌,当我打电话给customerApi 时它可能已经过期。如果令牌已过期,customerApi 将返回 401 响应。

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

获取Bearer令牌的方法:

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

而webClient 的用法是调用customerApi(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 的解决方案)

【问题讨论】:

标签: spring-webflux spring-webclient reactor-netty


【解决方案1】:

方法

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()));
}

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

【讨论】:

  • 完美解决方案!非常感谢,出于不正确的目的,与我需要实现的真实用例相比,我简化了主题。
  • 确实不错的解决方案,非常感谢@Isank
猜你喜欢
  • 1970-01-01
  • 2021-01-31
  • 2020-01-17
  • 2020-09-16
  • 1970-01-01
  • 2021-02-12
  • 1970-01-01
  • 2020-07-18
  • 2020-04-29
相关资源
最近更新 更多