web-dev-qa-db-fra.com

Ajout d'une nouvelle tentative de toutes les demandes de WebClient

nous avons un serveur pour récupérer un jeton OAUTH, et le jeton oauth est ajouté à chaque demande via la méthode WebClient.filter, par exemple

webClient
                .mutate()
                .filter((request, next) -> tokenProvider.getBearerToken()
                        .map(token -> ClientRequest.from(request)
                                .headers(httpHeaders -> httpHeaders.set("Bearer", token))
                                .build()).flatMap(next::exchange))
                .build();
TokenProvider.getBearerToken returns Mono<String> since it is a webclient request (this is cached)

Je veux avoir une fonctionnalité de nouvelle tentative qui, en cas d'erreur 401, invalidera le jeton et réessayera la demande.

webClient.post()
            .uri(properties.getServiceRequestUrl())
            .contentType(MediaType.APPLICATION_JSON)
            .body(fromObject(createRequest))
            .retrieve()
            .bodyToMono(MyResponseObject.class)
            .retryWhen(retryOnceOn401(provider))

private Retry<Object> retryOnceOn401(TokenProvider tokenProvider) {
        return Retry.onlyIf(context -> context.exception() instanceof WebClientResponseException && ((WebClientResponseException) context.exception()).getStatusCode() == HttpStatus.UNAUTHORIZED)
                .doOnRetry(objectRetryContext -> tokenProvider.invalidate());
    }

existe-t-il un moyen de déplacer cela vers la fonction webClient.mutate () ..... build ()? pour que toutes les demandes aient cette fonction de nouvelle tentative?

J'ai essayé d'ajouter en tant que filtre, mais cela ne semble pas fonctionner, par exemple.

.filter(((request, next) -> next.exchange(request).retryWhen(retryOnceOn401(tokenProvider))))

des suggestions sur la meilleure façon d'aborder cela? Cordialement

8
Kevin Hussey

J'ai compris cela, ce qui était évident après avoir vu que retry ne fonctionne que sur les exceptions, webClient ne lève pas l'exception, car l'objet clientResponse contient juste la réponse, uniquement lorsque bodyTo est appelé, l'exception est levée sur le statut http, donc pour résoudre ce problème , on peut imiter ce comportement

@Bean(name = "retryWebClient")
    public WebClient retryWebClient(WebClient.Builder builder, TokenProvider tokenProvider) {
        return builder.baseUrl("http://localhost:8080")
                .filter((request, next) ->
                        next.exchange(request)
                            .doOnNext(clientResponse -> {
                                    if (clientResponse.statusCode() == HttpStatus.UNAUTHORIZED) {
                                        throw new RuntimeException();
                                    }
                            }).retryWhen(Retry.anyOf(RuntimeException.class)
                                .doOnRetry(objectRetryContext -> tokenProvider.expire())
                                .retryOnce())

                ).build();
    }

MODIFIER l'une des fonctionnalités avec la répétition/la nouvelle tentative est que cela ne change pas la demande d'origine, dans mon cas, j'avais besoin de récupérer un nouveau jeton OAuth, mais ce qui précède a envoyé la même chose (expiré J'ai trouvé un moyen de le faire en utilisant un filtre d'échange, une fois OAuth le mot de passe est dans spring-security-2.0, je devrais pouvoir l'intégrer avec AccessTokens, etc., mais dans pendant ce temps

ExchangeFilterFunction retryOn401Function(TokenProvider tokenProvider) {
        return (request, next) -> next.exchange(request)
                .flatMap((Function<ClientResponse, Mono<ClientResponse>>) clientResponse -> {
                    if (clientResponse.statusCode().value() == 401) {
                        ClientRequest retryRequest = ClientRequest.from(request).header("Authorization", "Bearer " + tokenProvider.getNewToken().toString()).build();
                        return next.exchange(retryRequest);
                    } else {
                        return Mono.just(clientResponse);
                    }
                });
    }
11
Kevin Hussey