diff --git a/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java b/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java index 11ecdb5795..e08ae70360 100644 --- a/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java +++ b/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java @@ -49,8 +49,8 @@ import org.springframework.util.ObjectUtils; * {@link #hasError(HttpStatusCode)}. Unknown status codes will be ignored by * {@link #hasError(ClientHttpResponse)}. * - *

See {@link #handleError(ClientHttpResponse)} for more details on specific - * exception types. + *

See {@link #handleError(URI, HttpMethod, ClientHttpResponse)} for more + * details on specific exception types. * * @author Arjen Poutsma * @author Rossen Stoyanchev @@ -116,27 +116,6 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { return (series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR); } - /** - * Handle the error in the given response with the given resolved status code. - *

The default implementation throws: - *

- * @throws UnknownHttpStatusCodeException in case of an unresolvable status code - * @see #handleError(ClientHttpResponse, HttpStatusCode, URI, HttpMethod) - */ - @Override - public void handleError(ClientHttpResponse response) throws IOException { - handleError(response, response.getStatusCode(), null, null); - } - /** * Handle the error in the given response with the given resolved status code * and extra information providing access to the request URL and HTTP method. @@ -157,9 +136,20 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { */ @Override public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { + handleError(response); handleError(response, response.getStatusCode(), url, method); } + /** + * {@inheritDoc} + *

As of 6.2.1 this method is a no-op unless overridden. + */ + @SuppressWarnings("removal") + @Override + public void handleError(ClientHttpResponse response) throws IOException { + // no-op, but here for backwards compatibility + } + /** * Handle the error based on the resolved status code. *

The default implementation delegates to diff --git a/spring-web/src/main/java/org/springframework/web/client/ExtractingResponseErrorHandler.java b/spring-web/src/main/java/org/springframework/web/client/ExtractingResponseErrorHandler.java index 578a124aba..e2c95a992b 100644 --- a/spring-web/src/main/java/org/springframework/web/client/ExtractingResponseErrorHandler.java +++ b/spring-web/src/main/java/org/springframework/web/client/ExtractingResponseErrorHandler.java @@ -42,11 +42,12 @@ import org.springframework.util.CollectionUtils; * mappings has a match for the {@linkplain ClientHttpResponse#getStatusCode() * status code} of a given {@code ClientHttpResponse}, * {@link #hasError(ClientHttpResponse)} will return {@code true}, and - * {@link #handleError(ClientHttpResponse)} will attempt to use the - * {@linkplain #setMessageConverters(List) configured message converters} to - * convert the response into the mapped subclass of {@link RestClientException}. - * Note that the {@linkplain #setStatusMapping(Map) status mapping} takes - * precedence over {@linkplain #setSeriesMapping(Map) series mapping}. + * {@link #handleError(ClientHttpResponse, HttpStatusCode, URI, HttpMethod)} + * will attempt to use the {@linkplain #setMessageConverters(List) configured + * message converters} to convert the response into the mapped subclass of + * {@link RestClientException}. Note that the + * {@linkplain #setStatusMapping(Map) status mapping} takes precedence over + * {@linkplain #setSeriesMapping(Map) series mapping}. * *

If there is no match, this error handler will default to the behavior of * {@link DefaultResponseErrorHandler}. Note that you can override this default @@ -98,9 +99,10 @@ public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler * If this mapping has a match * for the {@linkplain ClientHttpResponse#getStatusCode() status code} of a given * {@code ClientHttpResponse}, {@link #hasError(ClientHttpResponse)} will return - * {@code true} and {@link #handleError(ClientHttpResponse)} will attempt to use the - * {@linkplain #setMessageConverters(List) configured message converters} to convert the - * response into the mapped subclass of {@link RestClientException}. + * {@code true} and {@link #handleError(ClientHttpResponse, HttpStatusCode, URI, HttpMethod)} + * will attempt to use the {@linkplain #setMessageConverters(List) configured + * message converters} to convert the response into the mapped subclass of + * {@link RestClientException}. */ public void setStatusMapping(Map> statusMapping) { if (!CollectionUtils.isEmpty(statusMapping)) { @@ -113,9 +115,10 @@ public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler * If this mapping has a match * for the {@linkplain ClientHttpResponse#getStatusCode() status code} of a given * {@code ClientHttpResponse}, {@link #hasError(ClientHttpResponse)} will return - * {@code true} and {@link #handleError(ClientHttpResponse)} will attempt to use the - * {@linkplain #setMessageConverters(List) configured message converters} to convert the - * response into the mapped subclass of {@link RestClientException}. + * {@code true} and {@link #handleError(ClientHttpResponse, HttpStatusCode, URI, HttpMethod)} + * will attempt to use the {@linkplain #setMessageConverters(List) configured + * message converters} to convert the response into the mapped subclass of + * {@link RestClientException}. */ public void setSeriesMapping(Map> seriesMapping) { if (!CollectionUtils.isEmpty(seriesMapping)) { diff --git a/spring-web/src/main/java/org/springframework/web/client/NoOpResponseErrorHandler.java b/spring-web/src/main/java/org/springframework/web/client/NoOpResponseErrorHandler.java index af0f22a52b..4d1498b403 100644 --- a/spring-web/src/main/java/org/springframework/web/client/NoOpResponseErrorHandler.java +++ b/spring-web/src/main/java/org/springframework/web/client/NoOpResponseErrorHandler.java @@ -43,9 +43,4 @@ public final class NoOpResponseErrorHandler implements ResponseErrorHandler { return false; } - @Override - public void handleError(ClientHttpResponse response) throws IOException { - // never actually called - } - } diff --git a/spring-web/src/main/java/org/springframework/web/client/ResponseErrorHandler.java b/spring-web/src/main/java/org/springframework/web/client/ResponseErrorHandler.java index db2329f8fe..4ca64425f8 100644 --- a/spring-web/src/main/java/org/springframework/web/client/ResponseErrorHandler.java +++ b/spring-web/src/main/java/org/springframework/web/client/ResponseErrorHandler.java @@ -45,14 +45,6 @@ public interface ResponseErrorHandler { * Handle the error in the given response. *

This method is only called when {@link #hasError(ClientHttpResponse)} * has returned {@code true}. - * @param response the response with the error - * @throws IOException in case of I/O errors - */ - void handleError(ClientHttpResponse response) throws IOException; - - /** - * Alternative to {@link #handleError(ClientHttpResponse)} with extra - * information providing access to the request URL and HTTP method. * @param url the request URL * @param method the HTTP method * @param response the response with the error @@ -63,4 +55,17 @@ public interface ResponseErrorHandler { handleError(response); } + /** + * Handle the error in the given response. + *

This method is only called when {@link #hasError(ClientHttpResponse)} + * has returned {@code true}. + * @param response the response with the error + * @throws IOException in case of I/O errors + * @deprecated in favor of {@link #handleError(URI, HttpMethod, ClientHttpResponse)} + */ + @Deprecated(since = "6.2.1", forRemoval = true) + default void handleError(ClientHttpResponse response) throws IOException { + // no-op unless overridden + } + } diff --git a/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerHttpStatusTests.java b/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerHttpStatusTests.java index 328d0259ec..e7d6a94eac 100644 --- a/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerHttpStatusTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerHttpStatusTests.java @@ -16,6 +16,7 @@ package org.springframework.web.client; +import java.net.URI; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; @@ -24,6 +25,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; @@ -80,7 +82,8 @@ class DefaultResponseErrorHandlerHttpStatusTests { given(this.response.getStatusCode()).willReturn(httpStatus); given(this.response.getHeaders()).willReturn(headers); - assertThatExceptionOfType(expectedExceptionClass).isThrownBy(() -> this.handler.handleError(this.response)); + assertThatExceptionOfType(expectedExceptionClass) + .isThrownBy(() -> this.handler.handleError(URI.create("/"), HttpMethod.GET, this.response)); } static Stream errorCodes() { diff --git a/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java b/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java index 576070361d..fbe2f9d318 100644 --- a/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java @@ -75,8 +75,8 @@ class DefaultResponseErrorHandlerTests { given(response.getBody()).willReturn(new ByteArrayInputStream("Hello World".getBytes(UTF_8))); assertThatExceptionOfType(HttpClientErrorException.class) - .isThrownBy(() -> handler.handleError(response)) - .withMessage("404 Not Found: \"Hello World\"") + .isThrownBy(() -> handler.handleError(URI.create("/"), HttpMethod.GET, response)) + .withMessage("404 Not Found on GET request for \"/\": \"Hello World\"") .satisfies(ex -> assertThat(ex.getResponseHeaders()).isEqualTo(headers)); } @@ -127,7 +127,8 @@ class DefaultResponseErrorHandlerTests { given(response.getHeaders()).willReturn(headers); given(response.getBody()).willThrow(new IOException()); - assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> handler.handleError(response)); + assertThatExceptionOfType(HttpClientErrorException.class) + .isThrownBy(() -> handler.handleError(URI.create("/"), HttpMethod.GET, response)); } @Test @@ -140,7 +141,7 @@ class DefaultResponseErrorHandlerTests { given(response.getHeaders()).willReturn(headers); assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> - handler.handleError(response)); + handler.handleError(URI.create("/"), HttpMethod.GET, response)); } @Test // SPR-16108 @@ -165,7 +166,7 @@ class DefaultResponseErrorHandlerTests { given(response.getHeaders()).willReturn(headers); assertThatExceptionOfType(UnknownHttpStatusCodeException.class).isThrownBy(() -> - handler.handleError(response)); + handler.handleError(URI.create("/"), HttpMethod.GET, response)); } @Test // SPR-17461 @@ -196,7 +197,7 @@ class DefaultResponseErrorHandlerTests { given(response.getHeaders()).willReturn(headers); given(response.getBody()).willReturn(body); - Throwable throwable = catchThrowable(() -> handler.handleError(response)); + Throwable throwable = catchThrowable(() -> handler.handleError(URI.create("/"), HttpMethod.GET, response)); // validate exception assertThat(throwable).isInstanceOf(HttpClientErrorException.class); @@ -236,7 +237,7 @@ class DefaultResponseErrorHandlerTests { given(response.getHeaders()).willReturn(headers); given(response.getBody()).willReturn(body); - Throwable throwable = catchThrowable(() -> handler.handleError(response)); + Throwable throwable = catchThrowable(() -> handler.handleError(URI.create("/"), HttpMethod.GET, response)); // validate exception assertThat(throwable).isInstanceOf(HttpServerErrorException.class); diff --git a/spring-web/src/test/java/org/springframework/web/client/ExtractingResponseErrorHandlerTests.java b/spring-web/src/test/java/org/springframework/web/client/ExtractingResponseErrorHandlerTests.java index a65951ea4f..1af5107112 100644 --- a/spring-web/src/test/java/org/springframework/web/client/ExtractingResponseErrorHandlerTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/ExtractingResponseErrorHandlerTests.java @@ -17,6 +17,7 @@ package org.springframework.web.client; import java.io.ByteArrayInputStream; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; @@ -26,6 +27,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; @@ -98,7 +100,7 @@ class ExtractingResponseErrorHandlerTests { given(this.response.getBody()).willReturn(new ByteArrayInputStream(body)); assertThatExceptionOfType(MyRestClientException.class) - .isThrownBy(() -> this.errorHandler.handleError(this.response)) + .isThrownBy(() -> this.errorHandler.handleError(URI.create("/"), HttpMethod.GET, this.response)) .satisfies(ex -> assertThat(ex.getFoo()).isEqualTo("bar")); } @@ -114,7 +116,7 @@ class ExtractingResponseErrorHandlerTests { given(this.response.getBody()).willReturn(new ByteArrayInputStream(body)); assertThatExceptionOfType(MyRestClientException.class) - .isThrownBy(() -> this.errorHandler.handleError(this.response)) + .isThrownBy(() -> this.errorHandler.handleError(URI.create("/"), HttpMethod.GET, this.response)) .satisfies(ex -> assertThat(ex.getFoo()).isEqualTo("bar")); } @@ -130,7 +132,7 @@ class ExtractingResponseErrorHandlerTests { given(this.response.getBody()).willReturn(new ByteArrayInputStream(body)); assertThatExceptionOfType(HttpClientErrorException.class) - .isThrownBy(() -> this.errorHandler.handleError(this.response)) + .isThrownBy(() -> this.errorHandler.handleError(URI.create("/"), HttpMethod.GET, this.response)) .satisfies(ex -> { assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); assertThat(ex.getResponseBodyAsByteArray()).isEqualTo(body); @@ -150,7 +152,7 @@ class ExtractingResponseErrorHandlerTests { responseHeaders.setContentLength(body.length); given(this.response.getBody()).willReturn(new ByteArrayInputStream(body)); - this.errorHandler.handleError(this.response); + this.errorHandler.handleError(URI.create("/"), HttpMethod.GET, this.response); } diff --git a/spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java b/spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java index 9a438ea866..1624f25719 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java @@ -345,12 +345,12 @@ class RestClientObservationTests { } @Override - public boolean hasError(ClientHttpResponse response) throws IOException { + public boolean hasError(ClientHttpResponse response) { return true; } @Override - public void handleError(ClientHttpResponse response) throws IOException { + public void handleError(URI uri, HttpMethod httpMethod, ClientHttpResponse response) { assertThat(this.observationRegistry.getCurrentObservationScope()).isNotNull(); } } diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateObservationTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateObservationTests.java index 9dff87051c..b8a2ad99d3 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateObservationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateObservationTests.java @@ -242,12 +242,12 @@ class RestTemplateObservationTests { } @Override - public boolean hasError(ClientHttpResponse response) throws IOException { + public boolean hasError(ClientHttpResponse response) { return true; } @Override - public void handleError(ClientHttpResponse response) throws IOException { + public void handleError(URI uri, HttpMethod httpMethod, ClientHttpResponse response) { currentObservation = this.observationRegistry.getCurrentObservation(); } }