From 23066731edcd8f517214d83588684cf216347c01 Mon Sep 17 00:00:00 2001 From: inabyss Date: Thu, 30 Apr 2020 06:15:41 +0900 Subject: [PATCH] Preserve Http Response Info When MessageConverter Not Found Case And Introduce Detail Purpose When Response Could Not Extract --- .../client/HttpMessageConverterExtractor.java | 25 +++++++++--- .../client/RestClientResponseException.java | 39 +++++++++++++++++++ .../HttpMessageConverterExtractorTests.java | 7 ++-- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/client/HttpMessageConverterExtractor.java b/spring-web/src/main/java/org/springframework/web/client/HttpMessageConverterExtractor.java index 3e640b2c9ae5..13e05185efae 100644 --- a/spring-web/src/main/java/org/springframework/web/client/HttpMessageConverterExtractor.java +++ b/spring-web/src/main/java/org/springframework/web/client/HttpMessageConverterExtractor.java @@ -17,7 +17,9 @@ package org.springframework.web.client; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.List; import org.apache.commons.logging.Log; @@ -31,6 +33,8 @@ import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.StreamUtils; + /** * Response extractor that uses the given {@linkplain HttpMessageConverter entity converters} @@ -116,12 +120,24 @@ public T extractData(ClientHttpResponse response) throws IOException { } } catch (IOException | HttpMessageNotReadableException ex) { - throw new RestClientException("Error while extracting response for type [" + - this.responseType + "] and content type [" + contentType + "]", ex); + throw new RestClientResponseException.FailedToReadResponseBody( + "Error while extracting response for type [" + this.responseType + "] and content type [" + contentType + "]", + response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), ex); } - throw new RestClientException("Could not extract response: no suitable HttpMessageConverter found " + - "for response type [" + this.responseType + "] and content type [" + contentType + "]"); + String defaultConverterNotFoundMessage = "Could not find suitable HttpMessageConverter to extract response " + + "for response type [" + this.responseType + "] and content type [" + contentType + "]"; + + try (InputStream is = responseWrapper.getBody()) { + byte[] bytes = StreamUtils.copyToByteArray(is); + throw new RestClientResponseException.MessageConverterNotFound(defaultConverterNotFoundMessage, + response.getRawStatusCode(),response.getStatusText(), response.getHeaders(), bytes, StandardCharsets.UTF_8); + } + catch (IOException ex) { + throw new RestClientResponseException.MessageConverterNotFound( + defaultConverterNotFoundMessage + ". and Error while preserve body. so there are no preserved body contents.", + response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), ex); + } } /** @@ -141,5 +157,4 @@ protected MediaType getContentType(ClientHttpResponse response) { } return contentType; } - } diff --git a/spring-web/src/main/java/org/springframework/web/client/RestClientResponseException.java b/spring-web/src/main/java/org/springframework/web/client/RestClientResponseException.java index 2a927f376905..97cd1b47fd4b 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestClientResponseException.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestClientResponseException.java @@ -23,6 +23,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.lang.Nullable; + /** * Common base class for exceptions that contain actual HTTP response data. * @@ -68,6 +69,15 @@ public RestClientResponseException(String message, int statusCode, String status this.responseCharset = (responseCharset != null ? responseCharset.name() : null); } + public RestClientResponseException(String message, int statusCode, String statusText, @Nullable HttpHeaders responseHeaders, Throwable ex){ + super(message, ex); + this.rawStatusCode = statusCode; + this.statusText = statusText; + this.responseHeaders = responseHeaders; + this.responseBody = new byte[0]; + this.responseCharset = null; + } + /** * Return the raw HTTP status code value. @@ -125,4 +135,33 @@ public String getResponseBodyAsString(Charset fallbackCharset) { } } + /** + * {@link RestClientResponseException} for can not find suitable message converter to extract body. + * @since 5.2.7 + */ + @SuppressWarnings("serial") + public static final class MessageConverterNotFound extends RestClientResponseException { + + public MessageConverterNotFound(String message, int statusCode, String statusText, HttpHeaders responseHeaders, @Nullable byte[] responseBody, @Nullable Charset responseCharset) { + + super(message, statusCode, statusText, responseHeaders, responseBody, responseCharset); + } + + public MessageConverterNotFound(String message, int statusCode, String statusText, HttpHeaders responseHeaders, Throwable ex) { + + super(message, statusCode, statusText, responseHeaders, ex); + } + } + + /** + * {@link RestClientResponseException} for exception raised when read body stream to extract body. + * @since 5.2.7 + */ + @SuppressWarnings("serial") + public static final class FailedToReadResponseBody extends RestClientResponseException { + + public FailedToReadResponseBody(String message, int statusCode, String statusText, HttpHeaders responseHeaders, Throwable ex) { + super(message, statusCode, statusText, responseHeaders, ex); + } + } } diff --git a/spring-web/src/test/java/org/springframework/web/client/HttpMessageConverterExtractorTests.java b/spring-web/src/test/java/org/springframework/web/client/HttpMessageConverterExtractorTests.java index 2374a0d95487..1ae5e484654c 100644 --- a/spring-web/src/test/java/org/springframework/web/client/HttpMessageConverterExtractorTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/HttpMessageConverterExtractorTests.java @@ -145,7 +145,8 @@ public void cannotRead() throws IOException { given(response.getHeaders()).willReturn(responseHeaders); given(response.getBody()).willReturn(new ByteArrayInputStream("Foobar".getBytes())); given(converter.canRead(String.class, contentType)).willReturn(false); - assertThatExceptionOfType(RestClientException.class).isThrownBy(() -> extractor.extractData(response)); + assertThatExceptionOfType(RestClientResponseException.class).isThrownBy(() -> extractor.extractData(response)) + .withMessageContaining("Could not find suitable HttpMessageConverter to extract response for response type [class java.lang.String] and content type [text/plain]"); } @Test @@ -177,7 +178,7 @@ public void converterThrowsIOException() throws IOException { given(response.getBody()).willReturn(new ByteArrayInputStream("Foobar".getBytes())); given(converter.canRead(String.class, contentType)).willReturn(true); given(converter.read(eq(String.class), any(HttpInputMessage.class))).willThrow(IOException.class); - assertThatExceptionOfType(RestClientException.class).isThrownBy(() -> extractor.extractData(response)) + assertThatExceptionOfType(RestClientResponseException.class).isThrownBy(() -> extractor.extractData(response)) .withMessageContaining("Error while extracting response for type [class java.lang.String] and content type [text/plain]") .withCauseInstanceOf(IOException.class); } @@ -189,7 +190,7 @@ public void converterThrowsHttpMessageNotReadableException() throws IOException given(response.getHeaders()).willReturn(responseHeaders); given(response.getBody()).willReturn(new ByteArrayInputStream("Foobar".getBytes())); given(converter.canRead(String.class, contentType)).willThrow(HttpMessageNotReadableException.class); - assertThatExceptionOfType(RestClientException.class).isThrownBy(() -> extractor.extractData(response)) + assertThatExceptionOfType(RestClientResponseException.class).isThrownBy(() -> extractor.extractData(response)) .withMessageContaining("Error while extracting response for type [class java.lang.String] and content type [text/plain]") .withCauseInstanceOf(HttpMessageNotReadableException.class); }