spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

Low level exception handling in HTTP Interface Clients

Open ZIRAKrezovic opened this issue 1 year ago • 0 comments

Affects: 6.1.11

I am following https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-http-interface-exceptions and I'm trying to map exceptions from all 3 supported adapter clients to ones that do not have a dependency on concrete web stack dependency.

For handling response from WebFlux WebClient, the following is recommended in the linked guide

By default, WebClient raises WebClientResponseException for 4xx and 5xx HTTP status codes. To customize this, register a response status handler that applies to all responses performed through the client:

WebClient webClient = WebClient.builder()
		.defaultStatusHandler(HttpStatusCode::isError, resp -> ...)
		.build();

However, this does not cover the low level exception , org.springframework.web.reactive.function.client.WebClientRequestException. Simplest way to trigger it is to give non-existing hostname as a URL.

Is there a possibility to somehow plug in into the exchange function and re-map the org.springframework.web.reactive.function.client.WebClientRequestException to something else? I am not able to find a way with Spring Framework 6.1.11

If I understand correctly, this is the equivalent of ResourceAccessException usually thrown in non-reactive RestClient(RestTemplate).

I have tried

var client = WebClient.builder()
                .filter(ExchangeFilterFunction.ofRequestProcessor(request -> Mono.just(request)))
                .filter(ExchangeFilterFunction.ofResponseProcessor(response -> Mono.just(response)))
                .defaultStatusHandler(HttpStatusCode::isError, SpringWebCompatibleStatusHandler::handle)
                .build();

var adapter =  WebClientAdapter.create(client);

Where SpringWebCompatibleStatusHandler is as follows

public final class SpringWebCompatibleStatusHandler {
    private SpringWebCompatibleStatusHandler() {}

    public static Mono<Throwable> handle(@NonNull ClientResponse response) {
        return response.createException().map(SpringWebCompatibleStatusHandler::map);
    }

    private static Throwable map(WebClientResponseException ex) {
        var statusCode = ex.getStatusCode();
        var statusText = ex.getStatusText();
        var headers = ex.getHeaders();
        var message = ex.getMessage();
        var body = ex.getResponseBodyAsByteArray();

        var charset =
                Optional.ofNullable(headers.getContentType())
                        .map(MediaType::getCharset)
                        .orElse(null);

        RestClientResponseException throwable;

        if (statusCode.is4xxClientError()) {
            throwable =
                    HttpClientErrorException.create(
                            message, statusCode, statusText, headers, body, charset);
        } else if (statusCode.is5xxServerError()) {
            throwable =
                    HttpServerErrorException.create(
                            message, statusCode, statusText, headers, body, charset);
        } else {
            throwable =
                    new UnknownHttpStatusCodeException(
                            message, statusCode.value(), statusText, headers, body, charset);
        }

        return throwable;
    }
}

None of the functions is called before or after I get WebClientRequestException

Caused by: org.springframework.web.reactive.function.client.WebClientRequestException: Failed to resolve 'ua-integration' [A(1)]
	at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:136)
	Suppressed: The stacktrace has been enhanced by Reactor, refer to additional information below: 
Assembly trace from producer [reactor.core.publisher.MonoErrorSupplied] :
	reactor.core.publisher.Mono.error(Mono.java:315)
	org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.wrapException(ExchangeFunctions.java:136)

ZIRAKrezovic avatar Aug 08 '24 15:08 ZIRAKrezovic