From 70cb81b4b5d9633d6eeb8967b7289fb9394734cd Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 24 Sep 2010 10:06:52 +0000 Subject: [PATCH] SPR-7591 - HttpStatusCodeException should contain response body --- .../client/DefaultResponseErrorHandler.java | 13 +++- .../web/client/HttpClientErrorException.java | 17 ++++++ .../web/client/HttpServerErrorException.java | 22 ++++++- .../web/client/HttpStatusCodeException.java | 59 +++++++++++++++++-- .../client/RestTemplateIntegrationTests.java | 24 ++++++-- 5 files changed, 122 insertions(+), 13 deletions(-) diff --git a/org.springframework.web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java b/org.springframework.web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java index ea2955bdbd1..d1ff7d99552 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java +++ b/org.springframework.web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -17,9 +17,12 @@ package org.springframework.web.client; import java.io.IOException; +import java.nio.charset.Charset; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; +import org.springframework.util.FileCopyUtils; /** * Default implementation of the {@link ResponseErrorHandler} interface. @@ -64,14 +67,18 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler { */ public void handleError(ClientHttpResponse response) throws IOException { HttpStatus statusCode = response.getStatusCode(); + MediaType contentType = response.getHeaders().getContentType(); + Charset charset = contentType != null ? contentType.getCharSet() : null; + byte[] body = FileCopyUtils.copyToByteArray(response.getBody()); switch (statusCode.series()) { case CLIENT_ERROR: - throw new HttpClientErrorException(statusCode, response.getStatusText()); + throw new HttpClientErrorException(statusCode, response.getStatusText(), body, charset); case SERVER_ERROR: - throw new HttpServerErrorException(statusCode, response.getStatusText()); + throw new HttpServerErrorException(statusCode, response.getStatusText(), body, charset); default: throw new RestClientException("Unknown status code [" + statusCode + "]"); } } + } diff --git a/org.springframework.web/src/main/java/org/springframework/web/client/HttpClientErrorException.java b/org.springframework.web/src/main/java/org/springframework/web/client/HttpClientErrorException.java index c9bd2c5eaa7..11f583de3b8 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/client/HttpClientErrorException.java +++ b/org.springframework.web/src/main/java/org/springframework/web/client/HttpClientErrorException.java @@ -16,6 +16,8 @@ package org.springframework.web.client; +import java.nio.charset.Charset; + import org.springframework.http.HttpStatus; /** @@ -44,4 +46,19 @@ public class HttpClientErrorException extends HttpStatusCodeException { super(statusCode, statusText); } + /** + * 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 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, + byte[] responseBody, + Charset responseCharset) { + super(statusCode, statusText, responseBody, responseCharset); + } } diff --git a/org.springframework.web/src/main/java/org/springframework/web/client/HttpServerErrorException.java b/org.springframework.web/src/main/java/org/springframework/web/client/HttpServerErrorException.java index 8733d2590e0..3828b036d8a 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/client/HttpServerErrorException.java +++ b/org.springframework.web/src/main/java/org/springframework/web/client/HttpServerErrorException.java @@ -16,19 +16,22 @@ package org.springframework.web.client; +import java.nio.charset.Charset; + import org.springframework.http.HttpStatus; /** * Exception thrown when an HTTP 5xx is received. * * @author Arjen Poutsma - * @since 3.0 * @see DefaultResponseErrorHandler + * @since 3.0 */ public class HttpServerErrorException extends HttpStatusCodeException { /** * Construct a new instance of {@code HttpServerErrorException} based on a {@link HttpStatus}. + * * @param statusCode the status code */ public HttpServerErrorException(HttpStatus statusCode) { @@ -37,6 +40,7 @@ public class HttpServerErrorException extends HttpStatusCodeException { /** * Construct a new instance of {@code HttpServerErrorException} based on a {@link HttpStatus} and status text. + * * @param statusCode the status code * @param statusText the status text */ @@ -44,4 +48,20 @@ public class HttpServerErrorException extends HttpStatusCodeException { super(statusCode, statusText); } + /** + * 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 responseBody the response body content, may be {@code null} + * @param responseCharset the response body charset, may be {@code null} + * @since 3.0.5 + */ + public HttpServerErrorException(HttpStatus statusCode, + String statusText, + byte[] responseBody, + Charset responseCharset) { + super(statusCode, statusText, responseBody, responseCharset); + } } diff --git a/org.springframework.web/src/main/java/org/springframework/web/client/HttpStatusCodeException.java b/org.springframework.web/src/main/java/org/springframework/web/client/HttpStatusCodeException.java index 86b4338e8db..3a9355a06af 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/client/HttpStatusCodeException.java +++ b/org.springframework.web/src/main/java/org/springframework/web/client/HttpStatusCodeException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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,9 @@ package org.springframework.web.client; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + import org.springframework.http.HttpStatus; /** @@ -26,33 +29,56 @@ import org.springframework.http.HttpStatus; */ public abstract class HttpStatusCodeException extends RestClientException { + private static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1"); + private final HttpStatus statusCode; private final String statusText; + private final byte[] responseBody; + + private final Charset responseCharset; /** * Construct a new instance of {@code HttpStatusCodeException} based on a {@link HttpStatus}. + * * @param statusCode the status code */ protected HttpStatusCodeException(HttpStatus statusCode) { - super(statusCode.toString()); - this.statusCode = statusCode; - this.statusText = statusCode.name(); + this(statusCode, statusCode.name(), null, null); } /** * Construct a new instance of {@code HttpStatusCodeException} based on a {@link HttpStatus} and status text. + * * @param statusCode the status code * @param statusText the status text */ protected HttpStatusCodeException(HttpStatus statusCode, String statusText) { + this(statusCode, statusText, null, null); + } + + /** + * 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 responseBody the response body content, may be {@code null} + * @param responseCharset the response body charset, may be {@code null} + * @since 3.0.5 + */ + protected HttpStatusCodeException(HttpStatus statusCode, + String statusText, + byte[] responseBody, + Charset responseCharset) { super(statusCode.value() + " " + statusText); this.statusCode = statusCode; this.statusText = statusText; + this.responseBody = responseBody != null ? responseBody : new byte[0]; + this.responseCharset = responseCharset != null ? responseCharset : DEFAULT_CHARSET; } - /** * Returns the HTTP status code. */ @@ -67,4 +93,27 @@ public abstract class HttpStatusCodeException extends RestClientException { return this.statusText; } + /** + * Returns the response body as a byte array. + * + * @since 3.0.5 + */ + public byte[] getResponseBodyAsByteArray() { + return responseBody; + } + + /** + * Returns the response body as a string. + * + * @since 3.0.5 + */ + public String getResponseBodyAsString() { + try { + return new String(responseBody, responseCharset.name()); + } + catch (UnsupportedEncodingException ex) { + // should not occur + throw new InternalError(ex.getMessage()); + } + } } diff --git a/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java b/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java index c6043c50c3e..4ec373c1e27 100644 --- a/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java +++ b/org.springframework.web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java @@ -145,14 +145,30 @@ public class RestTemplateIntegrationTests { assertEquals("Invalid content", helloWorld, s); } - @Test(expected = HttpClientErrorException.class) + @Test public void notFound() { - template.execute(URI + "/errors/notfound", HttpMethod.GET, null, null); + try { + template.execute(URI + "/errors/notfound", HttpMethod.GET, null, null); + fail("HttpClientErrorException expected"); + } + catch (HttpClientErrorException ex) { + assertEquals(HttpStatus.NOT_FOUND, ex.getStatusCode()); + assertNotNull(ex.getStatusText()); + assertNotNull(ex.getResponseBodyAsString()); + } } - @Test(expected = HttpServerErrorException.class) + @Test public void serverError() { - template.execute(URI + "/errors/server", HttpMethod.GET, null, null); + try { + template.execute(URI + "/errors/server", HttpMethod.GET, null, null); + fail("HttpServerErrorException expected"); + } + catch (HttpServerErrorException ex) { + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, ex.getStatusCode()); + assertNotNull(ex.getStatusText()); + assertNotNull(ex.getResponseBodyAsString()); + } } @Test