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..771a444bd731 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -31,6 +31,7 @@ import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.FileCopyUtils; /** * Response extractor that uses the given {@linkplain HttpMessageConverter entity converters} @@ -120,17 +121,17 @@ public T extractData(ClientHttpResponse response) throws IOException { this.responseType + "] and content type [" + contentType + "]", ex); } - throw new RestClientException("Could not extract response: no suitable HttpMessageConverter found " + - "for response type [" + this.responseType + "] and content type [" + contentType + "]"); + throw new UnknownContentTypeException(this.responseType, contentType, + response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), + getResponseBody(response)); } /** * Determine the Content-Type of the response based on the "Content-Type" * header or otherwise default to {@link MediaType#APPLICATION_OCTET_STREAM}. * @param response the response - * @return the MediaType, possibly {@code null}. + * @return the MediaType, or "application/octet-stream" */ - @Nullable protected MediaType getContentType(ClientHttpResponse response) { MediaType contentType = response.getHeaders().getContentType(); if (contentType == null) { @@ -142,4 +143,13 @@ protected MediaType getContentType(ClientHttpResponse response) { return contentType; } + private static byte[] getResponseBody(ClientHttpResponse response) { + try { + return FileCopyUtils.copyToByteArray(response.getBody()); + } + catch (IOException ex) { + // ignore + } + return new byte[0]; + } } diff --git a/spring-web/src/main/java/org/springframework/web/client/UnknownContentTypeException.java b/spring-web/src/main/java/org/springframework/web/client/UnknownContentTypeException.java new file mode 100644 index 000000000000..fca905394c82 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/client/UnknownContentTypeException.java @@ -0,0 +1,128 @@ +/* + * Copyright 2002-2020 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.client; + +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.lang.Nullable; + +/** + * Raised when no suitable + * {@link org.springframework.http.converter.HttpMessageConverter} could be + * found to extract the response. + * + * @author Rossen Stoyanchev + * @since 5.2.7 + */ +public class UnknownContentTypeException extends RestClientException { + + private static final long serialVersionUID = 2759516676367274084L; + + + private final Type targetType; + + private final MediaType contentType; + + private final int rawStatusCode; + + private final String statusText; + + private final byte[] responseBody; + + private final HttpHeaders responseHeaders; + + + /** + * Construct a new instance of with the given response data. + * @param targetType the expected target type + * @param contentType the content type of the response + * @param statusCode the raw status code value + * @param statusText the status text + * @param responseHeaders the response headers (may be {@code null}) + * @param responseBody the response body content (may be {@code null}) + */ + public UnknownContentTypeException(Type targetType, MediaType contentType, + int statusCode, String statusText, HttpHeaders responseHeaders, byte[] responseBody) { + + super("Could not extract response: no suitable HttpMessageConverter found " + + "for response type [" + targetType + "] and content type [" + contentType + "]"); + + this.targetType = targetType; + this.contentType = contentType; + this.rawStatusCode = statusCode; + this.statusText = statusText; + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + + /** + * Return the target type expected for the response. + */ + public Type getTargetType() { + return this.targetType; + } + + /** + * Return the content type of the response, or "application/octet-stream". + */ + public MediaType getContentType() { + return this.contentType; + } + + /** + * Return the raw HTTP status code value. + */ + public int getRawStatusCode() { + return this.rawStatusCode; + } + + /** + * Return the HTTP status text. + */ + public String getStatusText() { + return this.statusText; + } + + /** + * Return the HTTP response headers. + */ + @Nullable + public HttpHeaders getResponseHeaders() { + return this.responseHeaders; + } + + /** + * Return the response body as a byte array. + */ + public byte[] getResponseBody() { + return this.responseBody; + } + + /** + * Return the response body converted to String using the charset from the + * response "Content-Type" or {@code "UTF-8"} otherwise. + */ + public String getResponseBodyAsString() { + return new String(this.responseBody, this.contentType.getCharset() != null ? + this.contentType.getCharset() : StandardCharsets.UTF_8); + } + +}