Browse Source

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
pull/87/head
Scott Andrews 14 years ago committed by Chris Beams
parent
commit
b992c3d3f2
  1. 6
      spring-web/src/main/java/org/springframework/http/HttpHeaders.java
  2. 8
      spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java
  3. 18
      spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java
  4. 17
      spring-web/src/main/java/org/springframework/web/client/HttpServerErrorException.java
  5. 34
      spring-web/src/main/java/org/springframework/web/client/HttpStatusCodeException.java
  6. 10
      spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java

6
spring-web/src/main/java/org/springframework/http/HttpHeaders.java

@ -16,6 +16,8 @@
package org.springframework.http; package org.springframework.http;
import java.io.Serializable;
import java.net.URI; import java.net.URI;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@ -57,7 +59,9 @@ import org.springframework.util.StringUtils;
* @author Arjen Poutsma * @author Arjen Poutsma
* @since 3.0 * @since 3.0
*/ */
public class HttpHeaders implements MultiValueMap<String, String> { public class HttpHeaders implements MultiValueMap<String, String>, Serializable {
private static final long serialVersionUID = -8578554704772377436L;
private static final String ACCEPT = "Accept"; private static final String ACCEPT = "Accept";

8
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.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.ClientHttpResponse;
@ -69,14 +70,15 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
*/ */
public void handleError(ClientHttpResponse response) throws IOException { public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = response.getStatusCode(); HttpStatus statusCode = response.getStatusCode();
MediaType contentType = response.getHeaders().getContentType(); HttpHeaders headers = response.getHeaders();
MediaType contentType = headers.getContentType();
Charset charset = contentType != null ? contentType.getCharSet() : null; Charset charset = contentType != null ? contentType.getCharSet() : null;
byte[] body = getResponseBody(response); byte[] body = getResponseBody(response);
switch (statusCode.series()) { switch (statusCode.series()) {
case CLIENT_ERROR: case CLIENT_ERROR:
throw new HttpClientErrorException(statusCode, response.getStatusText(), body, charset); throw new HttpClientErrorException(statusCode, response.getStatusText(), headers, body, charset);
case SERVER_ERROR: case SERVER_ERROR:
throw new HttpServerErrorException(statusCode, response.getStatusText(), body, charset); throw new HttpServerErrorException(statusCode, response.getStatusText(), headers, body, charset);
default: default:
throw new RestClientException("Unknown status code [" + statusCode + "]"); throw new RestClientException("Unknown status code [" + statusCode + "]");
} }

18
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 java.nio.charset.Charset;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
/** /**
@ -29,7 +30,7 @@ import org.springframework.http.HttpStatus;
*/ */
public class HttpClientErrorException extends HttpStatusCodeException { 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); 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);
}
} }

17
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 java.nio.charset.Charset;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
/** /**
@ -29,7 +30,7 @@ import org.springframework.http.HttpStatus;
*/ */
public class HttpServerErrorException extends HttpStatusCodeException { 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); 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);
}
} }

34
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.io.UnsupportedEncodingException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
/** /**
@ -30,7 +31,7 @@ import org.springframework.http.HttpStatus;
*/ */
public abstract class HttpStatusCodeException extends RestClientException { 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"; 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 byte[] responseBody;
private final HttpHeaders responseHeaders;
private final String responseCharset; private final String responseCharset;
@ -49,7 +52,7 @@ public abstract class HttpStatusCodeException extends RestClientException {
* @param statusCode the status code * @param statusCode the status code
*/ */
protected HttpStatusCodeException(HttpStatus statusCode) { 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 * @param statusText the status text
*/ */
protected HttpStatusCodeException(HttpStatus statusCode, String statusText) { 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, String statusText,
byte[] responseBody, byte[] responseBody,
Charset responseCharset) { 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); super(statusCode.value() + " " + statusText);
this.statusCode = statusCode; this.statusCode = statusCode;
this.statusText = statusText; this.statusText = statusText;
this.responseHeaders = responseHeaders;
this.responseBody = responseBody != null ? responseBody : new byte[0]; this.responseBody = responseBody != null ? responseBody : new byte[0];
this.responseCharset = responseCharset != null ? responseCharset.name() : DEFAULT_CHARSET; this.responseCharset = responseCharset != null ? responseCharset.name() : DEFAULT_CHARSET;
} }
@ -97,8 +116,17 @@ public abstract class HttpStatusCodeException extends RestClientException {
return this.statusText; 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. * Return the response body as a byte array.
*
* @since 3.0.5 * @since 3.0.5
*/ */
public byte[] getResponseBodyAsByteArray() { public byte[] getResponseBodyAsByteArray() {

10
spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java

@ -68,7 +68,7 @@ public class DefaultResponseErrorHandlerTests {
verify(response); verify(response);
} }
@Test(expected = HttpClientErrorException.class) @Test
public void handleError() throws Exception { public void handleError() throws Exception {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN); headers.setContentType(MediaType.TEXT_PLAIN);
@ -80,7 +80,13 @@ public class DefaultResponseErrorHandlerTests {
replay(response); replay(response);
handler.handleError(response); try {
handler.handleError(response);
fail("expected HttpClientErrorException");
}
catch (HttpClientErrorException e) {
assertSame(headers, e.getResponseHeaders());
}
verify(response); verify(response);
} }

Loading…
Cancel
Save