Beware of using spring WebClient(Episode4)
Change flatMap to map and onErrorResume to onErrorMap
AS-IS
Let's see the below code. I use clientResponse.createException().flatMap()
and onErrorResume
. However That is inefficient code. because flatMap unwrap and wrap to Mono.error
again. onErrorResume
is likewise.
.retrieve()
.onStatus(
httpStatus -> httpStatus != HttpStatus.OK,
clientResponse -> {
return clientResponse.createException()
.flatMap(it -> Mono.error(new RuntimeException("code : " + clientResponse.statusCode())));
})
.bodyToMono(String.class)
.onErrorResume(throwable -> {
return Mono.error(new RuntimeException(throwable));
});
TO-BE
So I can change flatMap to map and onErrorResume to onErrorMap. I remove inefficient code wrapping.
.retrieve()
.onStatus(
httpStatus -> httpStatus != HttpStatus.OK,
clientResponse -> {
return clientResponse.createException()
.map(it -> new RuntimeException("code : " + clientResponse.statusCode()));
})
.bodyToMono(String.class)
.onErrorMap(throwable -> {
return new RuntimeException(throwable);
});
※Beware
We have to use flatMap when we use exchangeToMono
or exchangeToFlux
.
Let's see the below code. If I use map operator, It returns Mono<RuntimeException>
type. However, clientResponse.bodyToMono(String.class)
returns Mono<String>
type so in that case, we have compile error.
.exchangeToMono(clientResponse -> {
if (clientResponse.rawStatusCode() == HttpStatus.BAD_REQUEST.value()) {
// It returns Mono<RuntimeException> type and makes compile error
clientResponse
.createException()
.map(it -> new RuntimeException("400!!"));
// Mono<String> type
return clientResponse
.createException()
.flatMap(it -> Mono.error(new RuntimeException("400!!")));
}
if (clientResponse.rawStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return clientResponse
.createException()
.flatMap(it -> Mono.error(new RuntimeException("500!!")));
}
// Mono<String> type(success)
return clientResponse.bodyToMono(String.class);
})
cf) Mono.error
maintains the original T type so Mono<String>
is return type.
public static <T> Mono<T> error(Throwable error) {
return onAssembly(new MonoError(error));
}
We can use onErrorMap
in other way. If the request fail because of timeout, WebClient retry 3 times. But If It failed 3 times all, RetryExhaustedException occurs.
And in that case, We get origin error using Throwable::getCause
.
.retryWhen(
Retry
.fixedDelay(3, Duration.ofMillis(500))
.filter(YBSUtils::isTimeoutError)
)
.onErrorMap(
Exceptions::isRetryExhausted,
Throwable::getCause
)
...