Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebClient - how to ignore a specific HTTP error

I'd like to create a Spring WebClient that ignores a specific HTTP error. From the documentation of WebClient.retrieve():

By default, 4xx and 5xx responses result in a WebClientResponseException. To customize error handling, use ResponseSpec.onStatus(Predicate, Function) handlers.

I want all calls through a WebClient instance to ignore the specific HTTP error. That is why onStatus() is of no use to me (it has to be set per response).

The best I could come up with is this:

        WebClient webClient = WebClient.builder().filter((request, next) -> {
            Mono<ClientResponse> response = next.exchange(request);
            response = response.onErrorResume(WebClientResponseException.class, ex -> {
                return ex.getRawStatusCode() == 418 ? Mono.empty() : Mono.error(ex);
            });
            return response;
        }).build();

        URI uri = UriComponentsBuilder.fromUriString("https://httpstat.us/418").build().toUri();
        webClient.get().uri(uri).retrieve().toBodilessEntity().block();

but it does throw the exception instead of ignoring it (the lambda passed to onErrorResume() is never called).

Edited: fixed the mistake pointed out by the first answer.

like image 451
Gbr Avatar asked Oct 19 '25 10:10

Gbr


2 Answers

After extensive debugging of spring-webflux 5.3.4 and with the help of some ideas by Martin Tarjányi, I've come to this as the only possible "solution":

WebClient webClient = WebClient.builder().filter((request, next) -> {
    return next.exchange(request).flatMap(res -> {
        if (res.rawStatusCode() == HttpStatus.I_AM_A_TEAPOT.value()) {
            res = res.mutate().rawStatusCode(299).build();
        }
        return Mono.just(res);
    });
}).build();

URI uri = UriComponentsBuilder.fromUriString("https://httpstat.us/418").build().toUri();
String body = webClient.get().uri(uri).retrieve().toEntity(String.class).block().getBody();

The background: I am migrating some code from RestTemplate to WebClient. The old code looks like this:

RestTemplate restTemplate = ...;
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        if (response.getRawStatusCode() == HttpStatus.I_AM_A_TEAPOT.value()) {
            return;
        }
        super.handleError(response);
    }
});

URI uri = UriComponentsBuilder.fromUriString("https://httpstat.us/418").build().toUri();
String body = restTemplate.getForEntity(uri, String.class).getBody();

I believe it is a straightforward and common case.

WebClient is not yet a 100% replacement for RestTemplate.

like image 191
Gbr Avatar answered Oct 22 '25 05:10

Gbr


UPDATE: Turns out this answer doesn't address the core problem of filtering out a specific status code, just addresses a general coding pattern.


The reason onErrorResume lambda is not called is that response.onErrorResume creates a brand new Mono and your code does not use the result (i.e. it's not assigned to the response variable), so in the end a Mono without the onErrorResume operator is returned.

Using Project Reactor it's usually a good practice to avoid declaring local Mono and Flux variables and use a single chain instead. This helps to avoid similar subtle bugs.

WebClient webClient = WebClient.builder()
        .filter((request, next) -> next.exchange(request)
                        .onErrorResume(WebClientResponseException.class, ex -> ex.getRawStatusCode() == 418 ? Mono.empty() : Mono.error(ex)))
        .build();
like image 43
Martin Tarjányi Avatar answered Oct 22 '25 04:10

Martin Tarjányi