From 9a8e59b0176d6ad9bd31c3e433cecfac4378bd83 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Thu, 31 May 2012 13:45:10 -0400 Subject: [PATCH] Added response HttpHeaders to HttpStatusCodeException and subclasses Default HTTP errors from RestTemplate now include the 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. * introduced HttpStatusCodeException#getResponseHeaders * added constructor with headers param for HttpStatusCodeException, HttpClientErrorException and HttpServerErrorException * preserved exsisting constructor signatures * marked HttpHeaders as Serializable * generated new serialVersionUID where needed Issue: SPR-7938 --- .../org/springframework/http/HttpHeaders.java | 7 +++- .../client/DefaultResponseErrorHandler.java | 10 +++-- .../web/client/HttpClientErrorException.java | 21 +++++++++- .../web/client/HttpServerErrorException.java | 22 ++++++++++- .../web/client/HttpStatusCodeException.java | 38 +++++++++++++++++-- .../DefaultResponseErrorHandlerTests.java | 14 +++++-- 6 files changed, 98 insertions(+), 14 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 2bc17bb2c7c4..f261a7a7514c 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -16,6 +16,7 @@ package org.springframework.http; +import java.io.Serializable; import java.net.URI; import java.nio.charset.Charset; import java.text.ParseException; @@ -54,7 +55,9 @@ * @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 0615f7a823a9..4514eb38023f 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -20,6 +20,7 @@ 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; @@ -68,14 +69,15 @@ protected boolean hasError(HttpStatus statusCode) { */ 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 99de72b16072..a5aaeedf7c5a 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 @@ import java.nio.charset.Charset; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; /** @@ -29,7 +30,7 @@ */ public class HttpClientErrorException extends HttpStatusCodeException { - private static final long serialVersionUID = 6777393766937023392L; + private static final long serialVersionUID = 5177019431887513952L; /** * Construct a new instance of {@code HttpClientErrorException} based on a {@link HttpStatus}. @@ -63,4 +64,22 @@ public HttpClientErrorException(HttpStatus statusCode, Charset responseCharset) { super(statusCode, statusText, responseBody, responseCharset); } + + /** + * Construct a new instance of {@code HttpClientErrorException} 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} + */ + 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 9549dcbd0e5a..7bd454b24b7e 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 @@ import java.nio.charset.Charset; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; /** @@ -29,7 +30,7 @@ */ public class HttpServerErrorException extends HttpStatusCodeException { - private static final long serialVersionUID = -2565832100451369997L; + private static final long serialVersionUID = -2915754006618138282L; /** * Construct a new instance of {@code HttpServerErrorException} based on a {@link HttpStatus}. @@ -66,4 +67,23 @@ public HttpServerErrorException(HttpStatus statusCode, Charset 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.0 + */ + 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 e2396e0683d3..1f75e6bcce2e 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 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; /** @@ -30,7 +31,7 @@ */ 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; /** @@ -48,7 +51,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); } /** @@ -58,7 +61,7 @@ protected HttpStatusCodeException(HttpStatus statusCode) { * @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,29 @@ protected HttpStatusCodeException(HttpStatus statusCode, String statusText, byte[] responseBody, Charset responseCharset) { + this(statusCode, statusText, null, responseBody, responseCharset); + } + + /** + * Construct a new instance of {@code HttpStatusCodeException} 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.0 + */ + 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; } @@ -96,6 +119,15 @@ public String getStatusText() { return this.statusText; } + /** + * Returns the HTTP response headers. + * + * @since 3.2.0 + */ + public HttpHeaders getResponseHeaders() { + return this.responseHeaders; + } + /** * Returns the response body as a byte array. * 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 0c9866c19bee..519f2cd5d44a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -29,7 +29,9 @@ import static org.easymock.EasyMock.*; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** @author Arjen Poutsma */ public class DefaultResponseErrorHandlerTests { @@ -64,7 +66,7 @@ public void hasErrorFalse() throws Exception { verify(response); } - @Test(expected = HttpClientErrorException.class) + @Test public void handleError() throws Exception { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.TEXT_PLAIN); @@ -76,7 +78,13 @@ public void handleError() throws Exception { replay(response); - handler.handleError(response); + try { + handler.handleError(response); + fail("expected HttpClientErrorException"); + } + catch (HttpClientErrorException e) { + assertSame(headers, e.getResponseHeaders()); + } verify(response); }