From 4972d18dd690ecae5bec86913d5b5fbe85c34fa8 Mon Sep 17 00:00:00 2001 From: jerzykrlk Date: Mon, 20 Aug 2018 21:15:02 +0200 Subject: [PATCH 1/2] Include URL and HTTP method in DefaultResponseErrorHandler See gh-28958 --- .../client/DefaultResponseErrorHandler.java | 59 +++++++++++++++++-- .../ExtractingResponseErrorHandler.java | 9 ++- .../DefaultResponseErrorHandlerTests.java | 18 ++++++ .../client/RestTemplateIntegrationTests.java | 4 +- 4 files changed, 82 insertions(+), 8 deletions(-) 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 bd1fc19e4f..90ca31b3c9 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 @@ package org.springframework.web.client; 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 java.util.function.Function; 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 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { * {@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: + *

+ * @throws UnknownHttpStatusCodeException in case of an unresolvable status code + * @see #handleError(URI, HttpMethod, ClientHttpResponse, HttpStatusCode) + */ + @Override + public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { HttpStatusCode statusCode = response.getStatusCode(); - handleError(response, statusCode); + handleError(url, method, response, statusCode); } /** @@ -144,10 +167,9 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { * */ private String getErrorMessage( - int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset) { - - String preface = rawStatusCode + " " + statusText + ": "; + int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset, @Nullable URI url, @Nullable HttpMethod method) { + String preface = getPreface(rawStatusCode, statusText, url, method); if (ObjectUtils.isEmpty(responseBody)) { return preface + "[no body]"; } @@ -160,6 +182,15 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { return preface + bodyText; } + private String getPreface(int rawStatusCode, String statusText, @Nullable URI url, @Nullable HttpMethod method) { + StringBuilder preface = new StringBuilder(rawStatusCode + " " + statusText); + if (!ObjectUtils.isEmpty(method) && !ObjectUtils.isEmpty(url)) { + preface.append(" after ").append(method).append(" ").append(url).append(" "); + } + preface.append(": "); + return preface.toString(); + } + /** * Handle the error based on the resolved status code. * @@ -172,11 +203,27 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { * @see HttpServerErrorException#create */ protected void handleError(ClientHttpResponse response, HttpStatusCode statusCode) throws IOException { + handleError(null, null, response, statusCode); + } + + /** + * Handle the error based on the resolved status code. + * + *

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 4357be4d27..533a20fc5f 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; @@ -149,8 +151,13 @@ public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler } } + @Override + public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { + handleError(response, response.getStatusCode()); + } + private void extract(@Nullable Class 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 640abf0b87..8c2622576f 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 @@ package org.springframework.web.client; 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 @@ class DefaultResponseErrorHandlerTests { .satisfies(ex -> assertThat(ex.getResponseHeaders()).isEqualTo(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 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 6f20c7ff11..30c5b23730 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 @@ -247,6 +247,7 @@ class RestTemplateIntegrationTests extends AbstractMockWebServerTests { 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]"); }); } @@ -259,7 +260,7 @@ class RestTemplateIntegrationTests extends AbstractMockWebServerTests { .satisfies(ex -> { assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); assumeFalse(clientHttpRequestFactory instanceof JdkClientHttpRequestFactory, "JDK HttpClient does not expose status text"); - 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]"); }); } @@ -273,6 +274,7 @@ class RestTemplateIntegrationTests extends AbstractMockWebServerTests { 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]"); }); } From f85c4e1ea714281c03287d1448dab9efcf81fc34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Tue, 30 Apr 2024 11:23:40 +0200 Subject: [PATCH 2/2] Polish "Include URL and HTTP method in DefaultResponseErrorHandler" See gh-28958 --- .../client/DefaultResponseErrorHandler.java | 82 +++++++++---------- .../ExtractingResponseErrorHandler.java | 18 ++-- .../DefaultResponseErrorHandlerTests.java | 42 +++++++--- .../client/RestTemplateIntegrationTests.java | 20 +++-- 4 files changed, 92 insertions(+), 70 deletions(-) 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 90ca31b3c9..e8f612c87a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -131,15 +131,17 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { * {@link HttpStatus} enum range. * * @throws UnknownHttpStatusCodeException in case of an unresolvable status code - * @see #handleError(URI, HttpMethod, ClientHttpResponse, HttpStatusCode) + * @see #handleError(ClientHttpResponse, HttpStatusCode, URI, HttpMethod) */ @Override public void handleError(ClientHttpResponse response) throws IOException { - handleError(null, null, response); + HttpStatusCode statusCode = response.getStatusCode(); + handleError(response, statusCode, null, null); } /** - * Handle the error in the given response with the given resolved status code. + * 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. *

The default implementation throws: *

* @throws UnknownHttpStatusCodeException in case of an unresolvable status code - * @see #handleError(URI, HttpMethod, ClientHttpResponse, HttpStatusCode) + * @since 6.2 + * @see #handleError(ClientHttpResponse, HttpStatusCode, URI, HttpMethod) */ @Override public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { HttpStatusCode statusCode = response.getStatusCode(); - handleError(url, method, response, statusCode); + handleError(response, statusCode, url, method); } /** * Return error message with details from the response body. For example: *
-	 * 404 Not Found: [{'id': 123, 'message': 'my message'}]
+	 * 404 Not Found on GET request for "https://example.com": [{'id': 123, 'message': 'my message'}]
 	 * 
*/ - private String getErrorMessage( - int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset, @Nullable URI url, @Nullable HttpMethod method) { + private String getErrorMessage(int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset, + @Nullable URI url, @Nullable HttpMethod method) { - String preface = getPreface(rawStatusCode, statusText, url, method); + StringBuilder msg = new StringBuilder(rawStatusCode + " " + statusText); + if (method != null) { + msg.append(" on ").append(method).append(" request"); + } + if (url != null) { + msg.append(" for \""); + String urlString = url.toString(); + int idx = urlString.indexOf('?'); + if (idx != -1) { + msg.append(urlString, 0, idx); + } + else { + msg.append(urlString); + } + msg.append("\""); + } + msg.append(": "); if (ObjectUtils.isEmpty(responseBody)) { - return preface + "[no body]"; + msg.append("[no body]"); } - - charset = (charset != null ? charset : StandardCharsets.UTF_8); - - String bodyText = new String(responseBody, charset); - bodyText = LogFormatUtils.formatValue(bodyText, -1, true); - - return preface + bodyText; - } - - private String getPreface(int rawStatusCode, String statusText, @Nullable URI url, @Nullable HttpMethod method) { - StringBuilder preface = new StringBuilder(rawStatusCode + " " + statusText); - if (!ObjectUtils.isEmpty(method) && !ObjectUtils.isEmpty(url)) { - preface.append(" after ").append(method).append(" ").append(url).append(" "); + else { + charset = (charset != null ? charset : StandardCharsets.UTF_8); + String bodyText = new String(responseBody, charset); + bodyText = LogFormatUtils.formatValue(bodyText, -1, true); + msg.append(bodyText); } - preface.append(": "); - return preface.toString(); + return msg.toString(); } /** @@ -198,27 +208,11 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { * {@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 + * @since 6.2 * @see HttpClientErrorException#create * @see HttpServerErrorException#create */ - protected void handleError(ClientHttpResponse response, HttpStatusCode statusCode) throws IOException { - handleError(null, null, response, statusCode); - } - - /** - * Handle the error based on the resolved status code. - * - *

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 { + protected void handleError(ClientHttpResponse response, HttpStatusCode statusCode, @Nullable URI url, @Nullable HttpMethod method) throws IOException { String statusText = response.getStatusText(); HttpHeaders headers = response.getHeaders(); byte[] body = getResponseBody(response); 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 533a20fc5f..c411b13377 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -138,7 +138,12 @@ public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler } @Override - public void handleError(ClientHttpResponse response, HttpStatusCode statusCode) throws IOException { + public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { + handleError(response, response.getStatusCode(), url, method); + } + + @Override + protected void handleError(ClientHttpResponse response, HttpStatusCode statusCode, @Nullable URI url, @Nullable HttpMethod method) throws IOException { if (this.statusMapping.containsKey(statusCode)) { extract(this.statusMapping.get(statusCode), response); } @@ -147,17 +152,12 @@ public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler extract(this.seriesMapping.get(series), response); } else { - super.handleError(response, statusCode); + super.handleError(response, statusCode, url, method); } } - @Override - public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { - handleError(response, response.getStatusCode()); - } - private void extract(@Nullable Class 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 8c2622576f..967b2c4fbe 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 @@ -29,6 +29,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; +import org.springframework.lang.Nullable; import org.springframework.util.StreamUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -80,19 +81,38 @@ class DefaultResponseErrorHandlerTests { } @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))); - + void handleErrorWithUrlAndMethod() throws Exception { + setupClientHttpResponse(HttpStatus.NOT_FOUND, "Hello World"); 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)); + .withMessage("404 Not Found on GET request for \"https://example.com\": \"Hello World\""); + } + + @Test + void handleErrorWithUrlAndQueryParameters() throws Exception { + setupClientHttpResponse(HttpStatus.NOT_FOUND, "Hello World"); + assertThatExceptionOfType(HttpClientErrorException.class) + .isThrownBy(() -> handler.handleError(URI.create("https://example.com/resource?access_token=123"), HttpMethod.GET, response)) + .withMessage("404 Not Found on GET request for \"https://example.com/resource\": \"Hello World\""); + } + + @Test + void handleErrorWithUrlAndNoBody() throws Exception { + setupClientHttpResponse(HttpStatus.NOT_FOUND, null); + assertThatExceptionOfType(HttpClientErrorException.class) + .isThrownBy(() -> handler.handleError(URI.create("https://example.com"), HttpMethod.GET, response)) + .withMessage("404 Not Found on GET request for \"https://example.com\": [no body]"); + } + + private void setupClientHttpResponse(HttpStatus status, @Nullable String textBody) throws Exception { + HttpHeaders headers = new HttpHeaders(); + given(response.getStatusCode()).willReturn(status); + given(response.getStatusText()).willReturn(status.getReasonPhrase()); + if (textBody != null) { + headers.setContentType(MediaType.TEXT_PLAIN); + given(response.getBody()).willReturn(new ByteArrayInputStream(textBody.getBytes(StandardCharsets.UTF_8))); + } + given(response.getHeaders()).willReturn(headers); } @Test 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 30c5b23730..05c9a100b5 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 @@ -241,13 +241,16 @@ class RestTemplateIntegrationTests extends AbstractMockWebServerTests { void notFound(ClientHttpRequestFactory clientHttpRequestFactory) { setUpClient(clientHttpRequestFactory); + String url = baseUrl + "/status/notfound"; assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> - template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, null)) + template.execute(url, HttpMethod.GET, null, null)) .satisfies(ex -> { 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]"); + assertThat(ex.getMessage()).containsSubsequence("404", "on GET request for \"" + url + "\": [no body]"); + assumeFalse(clientHttpRequestFactory instanceof JdkClientHttpRequestFactory, "JDK HttpClient does not expose status text"); + assertThat(ex.getMessage()).isEqualTo("404 Client Error on GET request for \"" + url + "\": [no body]"); }); } @@ -255,12 +258,14 @@ class RestTemplateIntegrationTests extends AbstractMockWebServerTests { void badRequest(ClientHttpRequestFactory clientHttpRequestFactory) { setUpClient(clientHttpRequestFactory); + String url = baseUrl + "/status/badrequest"; assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> - template.execute(baseUrl + "/status/badrequest", HttpMethod.GET, null, null)) + template.execute(url, HttpMethod.GET, null, null)) .satisfies(ex -> { assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThat(ex.getMessage()).containsSubsequence("400", "on GET request for \""+url+ "\": [no body]"); assumeFalse(clientHttpRequestFactory instanceof JdkClientHttpRequestFactory, "JDK HttpClient does not expose status text"); - assertThat(ex.getMessage()).isEqualTo("400 Client Error after GET http://localhost:" + port + "/status/badrequest : [no body]"); + assertThat(ex.getMessage()).isEqualTo("400 Client Error on GET request for \""+url+ "\": [no body]"); }); } @@ -268,13 +273,16 @@ class RestTemplateIntegrationTests extends AbstractMockWebServerTests { void serverError(ClientHttpRequestFactory clientHttpRequestFactory) { setUpClient(clientHttpRequestFactory); + String url = baseUrl + "/status/server"; assertThatExceptionOfType(HttpServerErrorException.class).isThrownBy(() -> - template.execute(baseUrl + "/status/server", HttpMethod.GET, null, null)) + template.execute(url, HttpMethod.GET, null, null)) .satisfies(ex -> { 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]"); + assertThat(ex.getMessage()).containsSubsequence("500", "on GET request for \"" + url + "\": [no body]"); + assumeFalse(clientHttpRequestFactory instanceof JdkClientHttpRequestFactory, "JDK HttpClient does not expose status text"); + assertThat(ex.getMessage()).isEqualTo("500 Server Error on GET request for \"" + url + "\": [no body]"); }); }