Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve Http Response Info When MessageConverter Not Found Case #24994

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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;
Expand All @@ -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}
Expand Down Expand Up @@ -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);
}
}

/**
Expand All @@ -141,5 +157,4 @@ protected MediaType getContentType(ClientHttpResponse response) {
}
return contentType;
}

}
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
}
}
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand Down