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 86589daaac01..e07792819669 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 @@ -19,6 +19,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Collections; @@ -28,6 +29,7 @@ import org.springframework.core.ResolvableType; import org.springframework.core.log.LogFormatUtils; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; @@ -129,12 +131,33 @@ protected boolean hasError(int statusCode) { * {@link HttpStatus} enum range. * * @throws UnknownHttpStatusCodeException in case of an unresolvable status code - * @see #handleError(ClientHttpResponse, HttpStatusCode) + * @see #handleError(URI, HttpMethod, ClientHttpResponse, HttpStatusCode) */ @Override public void handleError(ClientHttpResponse response) throws IOException { + handleError(null, null, response); + } + + /** + * Handle the error in the given response with the given resolved status code. + *
The default implementation throws: + *
The default implementation delegates to + * {@link HttpClientErrorException#create} for errors in the 4xx range, to + * {@link HttpServerErrorException#create} for errors in the 5xx range, + * or otherwise raises {@link UnknownHttpStatusCodeException}. + * @since 5.0 + * @see HttpClientErrorException#create + * @see HttpServerErrorException#create + */ + protected void handleError(@Nullable URI url, @Nullable HttpMethod method, ClientHttpResponse response, + HttpStatusCode statusCode) throws IOException { String statusText = response.getStatusText(); HttpHeaders headers = response.getHeaders(); byte[] body = getResponseBody(response); Charset charset = getCharset(response); - String message = getErrorMessage(statusCode.value(), statusText, body, charset); + String message = getErrorMessage(statusCode.value(), statusText, body, charset, url, method); RestClientResponseException ex; if (statusCode.is4xxClientError()) { 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 72cd840a3035..e35a5e673c50 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 @@ -17,11 +17,13 @@ package org.springframework.web.client; import java.io.IOException; +import java.net.URI; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; import org.springframework.http.client.ClientHttpResponse; @@ -148,8 +150,13 @@ public void handleError(ClientHttpResponse response, HttpStatusCode statusCode) } } + @Override + public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { + handleError(response, response.getStatusCode()); + } + private void extract(@Nullable Class extends RestClientException> exceptionClass, - ClientHttpResponse response) throws IOException { + ClientHttpResponse response) throws IOException { if (exceptionClass == null) { return; 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 ba6af327ed7c..57db498d14ce 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 @@ -18,11 +18,13 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.net.URI; import java.nio.charset.StandardCharsets; 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.HttpStatusCode; import org.springframework.http.MediaType; @@ -77,6 +79,22 @@ public void handleError() throws Exception { .satisfies(ex -> assertThat(ex.getResponseHeaders()).isSameAs(headers)); } + @Test + public void handleErrorWithUrlAndMethod() throws Exception { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.TEXT_PLAIN); + + given(response.getStatusCode()).willReturn(HttpStatus.NOT_FOUND); + given(response.getStatusText()).willReturn("Not Found"); + given(response.getHeaders()).willReturn(headers); + given(response.getBody()).willReturn(new ByteArrayInputStream("Hello World".getBytes(StandardCharsets.UTF_8))); + + assertThatExceptionOfType(HttpClientErrorException.class) + .isThrownBy(() -> handler.handleError(URI.create("https://example.com"), HttpMethod.GET, response)) + .withMessage("404 Not Found after GET https://example.com : \"Hello World\"") + .satisfies(ex -> assertThat(ex.getResponseHeaders()).isSameAs(headers)); + } + @Test public void handleErrorIOException() throws Exception { HttpHeaders headers = new HttpHeaders(); diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java index b1076e844e20..5b0c0bd7da4a 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java @@ -242,6 +242,7 @@ void notFound(ClientHttpRequestFactory clientHttpRequestFactory) { assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); assertThat(ex.getStatusText()).isNotNull(); assertThat(ex.getResponseBodyAsString()).isNotNull(); + assertThat(ex.getMessage()).isEqualTo("404 Client Error after GET http://localhost:" + port + "/status/notfound : [no body]"); }); } @@ -253,7 +254,7 @@ void badRequest(ClientHttpRequestFactory clientHttpRequestFactory) { template.execute(baseUrl + "/status/badrequest", HttpMethod.GET, null, null)) .satisfies(ex -> { assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); - assertThat(ex.getMessage()).isEqualTo("400 Client Error: [no body]"); + assertThat(ex.getMessage()).isEqualTo("400 Client Error after GET http://localhost:" + port + "/status/badrequest : [no body]"); }); } @@ -267,6 +268,7 @@ void serverError(ClientHttpRequestFactory clientHttpRequestFactory) { assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); assertThat(ex.getStatusText()).isNotNull(); assertThat(ex.getResponseBodyAsString()).isNotNull(); + assertThat(ex.getMessage()).isEqualTo("500 Server Error after GET http://localhost:" + port + "/status/server : [no body]"); }); }