I am currently migrating an old declarative Feign client to Spring Web. The old client uses checked exceptions, and I would like to keep that behavior (it is very useful to be able to immediately know what kind of problems you might encounter when invoking the client, IDE suggesting which exceptions you need to catch etc.).

In Feign my client looked like this:

public interface SomeClientInterface {
    @RequestLine("GET /some/path")
    ReturnType someMethod() throws CheckedException1, CheckedException2;
}

Feign's ErrorDecoder interface has the method Exception decode(String methodKey, Response response), which allows to decode server response into any checked exception.

The same client in Spring Web looks like this:

@HttpExchange
public interface SomeClientInterface {
    @GetExchange("/some/path")
    ReturnType someMethod() throws CheckedException1, CheckedException2;
}

Hovewer, Spring's ResponseErrorHandler only allows to throw IOException and unchecked exceptions (void handleError(ClientHttpResponse response) throws IOException). The client is created using RestClient.Builder with custom defaultStatusHandler, and HttpServiceProxyFactory.

I've been able to work around the issue by using lombok's @SneakyThrows. But I think it would make much more sense to allow custom checked exceptions in ResponseErrorHandler, possibly by changing handleError to be able to throw Exception, or by giving it return type Exception (or Optional<Exception>) and using the returned value as an exception thrown by invoking the client.

Comment From: rstoyanchev

It's not just ResponseErrorHandler that would need to change. A checked exception would have to propagate all the way out of HttpServiceProxyFactory, and that's quite a few levels:

org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 Server Error on GET request for "http://view-localhost:44457/greeting": "Hello Spring!"
    at org.springframework.web.client.HttpServerErrorException.create(HttpServerErrorException.java:102)
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:228)
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:163)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:953)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:902)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:751)
    at org.springframework.web.client.support.RestTemplateAdapter.exchangeForBody(RestTemplateAdapter.java:76)
    at org.springframework.web.service.invoker.HttpServiceMethod$ExchangeResponseFunction.lambda$create$4(HttpServiceMethod.java:440)
    at org.springframework.web.service.invoker.HttpServiceMethod$ExchangeResponseFunction.execute(HttpServiceMethod.java:397)
    at org.springframework.web.service.invoker.HttpServiceMethod.invoke(HttpServiceMethod.java:134)
    at org.springframework.web.service.invoker.HttpServiceProxyFactory$HttpServiceMethodInterceptor.invoke(HttpServiceProxyFactory.java:243)

It's the reason we don't propagate checked exceptions. They get declared on all levels while most levels only need to propagate but not handle them. They end up being generalized or wrapped as runtime exceptions anyway, and handled inconsistently, and effectively ignored.

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.