From b992c3d3f27b44fe3203012e636ac7dca3cf4858 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Thu, 31 May 2012 13:45:10 -0400 Subject: [PATCH] Include response headers in RestTemplate exceptions Default HTTP error exceptions thrown from RestTemplate now include response headers in addition to the response body. In particular, this enables inspection of the Content-Type header allowing manual deserialization of the response body without guessing as to the content type. - introduce HttpStatusCodeException#getResponseHeaders - add constructor with headers param for HttpStatusCodeException, HttpClientErrorException and HttpServerErrorException - preserve exsisting constructor signatures - mark HttpHeaders as Serializable - generate new serialVersionUID where needed Issue: SPR-7938 --- .../org/springframework/http/HttpHeaders.java | 6 +++- .../client/DefaultResponseErrorHandler.java | 8 +++-- .../web/client/HttpClientErrorException.java | 18 +++++++++- .../web/client/HttpServerErrorException.java | 17 +++++++++- .../web/client/HttpStatusCodeException.java | 34 +++++++++++++++++-- .../DefaultResponseErrorHandlerTests.java | 10 ++++-- 6 files changed, 82 insertions(+), 11 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index db44db0c80f..052c1f3408c 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -16,6 +16,8 @@ package org.springframework.http; +import java.io.Serializable; + import java.net.URI; import java.nio.charset.Charset; @@ -57,7 +59,9 @@ import org.springframework.util.StringUtils; * @author Arjen Poutsma * @since 3.0 */ -public class HttpHeaders implements MultiValueMap { +public class HttpHeaders implements MultiValueMap, Serializable { + + private static final long serialVersionUID = -8578554704772377436L; private static final String ACCEPT = "Accept"; 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 924f025384e..50b291cf4ec 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 @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; @@ -69,14 +70,15 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { */ public void handleError(ClientHttpResponse response) throws IOException { HttpStatus statusCode = response.getStatusCode(); - MediaType contentType = response.getHeaders().getContentType(); + HttpHeaders headers = response.getHeaders(); + MediaType contentType = headers.getContentType(); Charset charset = contentType != null ? contentType.getCharSet() : null; byte[] body = getResponseBody(response); switch (statusCode.series()) { case CLIENT_ERROR: - throw new HttpClientErrorException(statusCode, response.getStatusText(), body, charset); + throw new HttpClientErrorException(statusCode, response.getStatusText(), headers, body, charset); case SERVER_ERROR: - throw new HttpServerErrorException(statusCode, response.getStatusText(), body, charset); + throw new HttpServerErrorException(statusCode, response.getStatusText(), headers, body, charset); default: throw new RestClientException("Unknown status code [" + statusCode + "]"); } diff --git a/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java b/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java index 05126bf00c2..5d5ef0b40e3 100644 --- a/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java +++ b/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java @@ -18,6 +18,7 @@ package org.springframework.web.client; import java.nio.charset.Charset; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; /** @@ -29,7 +30,7 @@ import org.springframework.http.HttpStatus; */ public class HttpClientErrorException extends HttpStatusCodeException { - private static final long serialVersionUID = 6777393766937023392L; + private static final long serialVersionUID = 5177019431887513952L; /** @@ -64,4 +65,19 @@ public class HttpClientErrorException extends HttpStatusCodeException { super(statusCode, statusText, responseBody, responseCharset); } + /** + * Construct a new instance of {@code HttpClientErrorException} based on an + * {@link HttpStatus}, status text, and response body content. + * @param statusCode the status code + * @param statusText the status text + * @param responseHeaders the response headers, may be {@code null} + * @param responseBody the response body content, may be {@code null} + * @param responseCharset the response body charset, may be {@code null} + * @since 3.2 + */ + public HttpClientErrorException(HttpStatus statusCode, String statusText, + HttpHeaders responseHeaders, byte[] responseBody, Charset responseCharset) { + super(statusCode, statusText, responseHeaders, responseBody, responseCharset); + } + } diff --git a/spring-web/src/main/java/org/springframework/web/client/HttpServerErrorException.java b/spring-web/src/main/java/org/springframework/web/client/HttpServerErrorException.java index d6ad9653898..07bd013a52d 100644 --- a/spring-web/src/main/java/org/springframework/web/client/HttpServerErrorException.java +++ b/spring-web/src/main/java/org/springframework/web/client/HttpServerErrorException.java @@ -18,6 +18,7 @@ package org.springframework.web.client; import java.nio.charset.Charset; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; /** @@ -29,7 +30,7 @@ import org.springframework.http.HttpStatus; */ public class HttpServerErrorException extends HttpStatusCodeException { - private static final long serialVersionUID = -2565832100451369997L; + private static final long serialVersionUID = -2915754006618138282L; /** @@ -65,4 +66,18 @@ public class HttpServerErrorException extends HttpStatusCodeException { super(statusCode, statusText, responseBody, responseCharset); } + /** + * Construct a new instance of {@code HttpServerErrorException} based on a + * {@link HttpStatus}, status text, and response body content. + * @param statusCode the status code + * @param statusText the status text + * @param responseHeaders the response headers, may be {@code null} + * @param responseBody the response body content, may be {@code null} + * @param responseCharset the response body charset, may be {@code null} + * @since 3.2 + */ + public HttpServerErrorException(HttpStatus statusCode, String statusText, + HttpHeaders responseHeaders, byte[] responseBody, Charset responseCharset) { + super(statusCode, statusText, responseHeaders, responseBody, responseCharset); + } } diff --git a/spring-web/src/main/java/org/springframework/web/client/HttpStatusCodeException.java b/spring-web/src/main/java/org/springframework/web/client/HttpStatusCodeException.java index 7ec048d4d10..106ec761d65 100644 --- a/spring-web/src/main/java/org/springframework/web/client/HttpStatusCodeException.java +++ b/spring-web/src/main/java/org/springframework/web/client/HttpStatusCodeException.java @@ -19,6 +19,7 @@ package org.springframework.web.client; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; /** @@ -30,7 +31,7 @@ import org.springframework.http.HttpStatus; */ public abstract class HttpStatusCodeException extends RestClientException { - private static final long serialVersionUID = 1549626836533638803L; + private static final long serialVersionUID = -5807494703720513267L; private static final String DEFAULT_CHARSET = "ISO-8859-1"; @@ -40,6 +41,8 @@ public abstract class HttpStatusCodeException extends RestClientException { private final byte[] responseBody; + private final HttpHeaders responseHeaders; + private final String responseCharset; @@ -49,7 +52,7 @@ public abstract class HttpStatusCodeException extends RestClientException { * @param statusCode the status code */ protected HttpStatusCodeException(HttpStatus statusCode) { - this(statusCode, statusCode.name(), null, null); + this(statusCode, statusCode.name(), null, null, null); } /** @@ -59,7 +62,7 @@ public abstract class HttpStatusCodeException extends RestClientException { * @param statusText the status text */ protected HttpStatusCodeException(HttpStatus statusCode, String statusText) { - this(statusCode, statusText, null, null); + this(statusCode, statusText, null, null, null); } /** @@ -75,9 +78,25 @@ public abstract class HttpStatusCodeException extends RestClientException { String statusText, byte[] responseBody, Charset responseCharset) { + this(statusCode, statusText, null, responseBody, responseCharset); + } + + /** + * Construct a new instance of {@code HttpStatusCodeException} based on an + * {@link HttpStatus}, status text, and response body content. + * @param statusCode the status code + * @param statusText the status text + * @param responseHeaders the response headers, may be {@code null} + * @param responseBody the response body content, may be {@code null} + * @param responseCharset the response body charset, may be {@code null} + * @since 3.2 + */ + protected HttpStatusCodeException(HttpStatus statusCode, String statusText, + HttpHeaders responseHeaders, byte[] responseBody, Charset responseCharset) { super(statusCode.value() + " " + statusText); this.statusCode = statusCode; this.statusText = statusText; + this.responseHeaders = responseHeaders; this.responseBody = responseBody != null ? responseBody : new byte[0]; this.responseCharset = responseCharset != null ? responseCharset.name() : DEFAULT_CHARSET; } @@ -97,8 +116,17 @@ public abstract class HttpStatusCodeException extends RestClientException { return this.statusText; } + /** + * Return the HTTP response headers. + * @since 3.2 + */ + public HttpHeaders getResponseHeaders() { + return this.responseHeaders; + } + /** * Return the response body as a byte array. + * * @since 3.0.5 */ public byte[] getResponseBodyAsByteArray() { 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 129a507543c..13231a431a5 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 @@ -68,7 +68,7 @@ public class DefaultResponseErrorHandlerTests { verify(response); } - @Test(expected = HttpClientErrorException.class) + @Test public void handleError() throws Exception { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.TEXT_PLAIN); @@ -80,7 +80,13 @@ public class DefaultResponseErrorHandlerTests { replay(response); - handler.handleError(response); + try { + handler.handleError(response); + fail("expected HttpClientErrorException"); + } + catch (HttpClientErrorException e) { + assertSame(headers, e.getResponseHeaders()); + } verify(response); }