diff --git a/spring-web/src/main/java/org/springframework/web/client/DefaultHttpErrorDetailsExtractor.java b/spring-web/src/main/java/org/springframework/web/client/DefaultHttpErrorDetailsExtractor.java deleted file mode 100644 index a1aefcabd315..000000000000 --- a/spring-web/src/main/java/org/springframework/web/client/DefaultHttpErrorDetailsExtractor.java +++ /dev/null @@ -1,138 +0,0 @@ -package org.springframework.web.client; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URI; -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; - -import org.jetbrains.annotations.NotNull; -import org.springframework.http.HttpMethod; -import org.springframework.lang.Nullable; - -/** - * Spring's default implementation of the {@link HttpErrorDetailsExtractor} interface. - * - *

This extractor will compose a short summary of the http error response, including: - *

- * - * An example: - *
- * 404 Not Found after GET http://example.com:8080/my-endpoint : [{'id': 123, 'message': 'my very long... (500 bytes)]
- * 
- * - * @author Jerzy Krolak - * @since 5.1 - * @see DefaultResponseErrorHandler#setHttpErrorDetailsExtractor(HttpErrorDetailsExtractor) - */ -public class DefaultHttpErrorDetailsExtractor implements HttpErrorDetailsExtractor { - - private static final int MAX_BODY_BYTES_LENGTH = 400; - - private static final int MAX_BODY_CHARS_LENGTH = 200; - - /** - * Assemble a short summary of the HTTP error response. - * @param rawStatusCode HTTP status code - * @param statusText HTTP status text - * @param responseBody response body - * @param responseCharset response charset - * @param url request URI - * @param method request method - * @return error details string. Example:
404 Not Found after GET http://example.com:8080/my-endpoint : [{'id': 123, 'message': 'my very long... (500 bytes)]
- */ - @Override - @NotNull - public String getErrorDetails(int rawStatusCode, String statusText, @Nullable byte[] responseBody, - @Nullable Charset responseCharset, @Nullable URI url, @Nullable HttpMethod method) { - - if (url == null || method == null) { - return getSimpleErrorDetails(rawStatusCode, statusText); - } - - return getCompleteErrorDetails(rawStatusCode, statusText, responseBody, responseCharset, url, method); - } - - @NotNull - private String getCompleteErrorDetails(int rawStatusCode, String statusText, @Nullable byte[] responseBody, - @Nullable Charset responseCharset, @Nullable URI url, @Nullable HttpMethod method) { - - StringBuilder result = new StringBuilder(); - - result.append(getSimpleErrorDetails(rawStatusCode, statusText)) - .append(" after ") - .append(method) - .append(" ") - .append(url) - .append(" : "); - - if (responseBody == null || responseBody.length == 0) { - result.append("[no body]"); - } - else { - result - .append("[") - .append(getResponseBody(responseBody, responseCharset)) - .append("]"); - } - - return result.toString(); - } - - @NotNull - private String getSimpleErrorDetails(int rawStatusCode, String statusText) { - return rawStatusCode + " " + statusText; - } - - private String getResponseBody(byte[] responseBody, @Nullable Charset responseCharset) { - Charset charset = getCharsetOrDefault(responseCharset); - if (responseBody.length < MAX_BODY_BYTES_LENGTH) { - return getCompleteResponseBody(responseBody, charset); - } - return getResponseBodyPreview(responseBody, charset); - } - - @NotNull - private String getCompleteResponseBody(byte[] responseBody, Charset responseCharset) { - return new String(responseBody, responseCharset); - } - - private String getResponseBodyPreview(byte[] responseBody, Charset responseCharset) { - try { - String bodyPreview = readBodyAsString(responseBody, responseCharset); - return bodyPreview + "... (" + responseBody.length + " bytes)"; - } - catch (IOException e) { - // should never happen - throw new IllegalStateException(e); - } - } - - @NotNull - private String readBodyAsString(byte[] responseBody, Charset responseCharset) throws IOException { - - Reader reader = new InputStreamReader(new ByteArrayInputStream(responseBody), responseCharset); - CharBuffer result = CharBuffer.allocate(MAX_BODY_CHARS_LENGTH); - - reader.read(result); - reader.close(); - result.flip(); - - return result.toString(); - } - - private Charset getCharsetOrDefault(@Nullable Charset responseCharset) { - if (responseCharset == null) { - return StandardCharsets.ISO_8859_1; - } - return responseCharset; - } - -} 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 8a9ebdeb1d59..ff72eb1e465d 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 @@ -16,18 +16,21 @@ package org.springframework.web.client; +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.net.URI; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.CharBuffer; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; +import org.springframework.util.ObjectUtils; /** * Spring's default implementation of the {@link ResponseErrorHandler} interface. @@ -46,17 +49,6 @@ */ public class DefaultResponseErrorHandler implements ResponseErrorHandler { - private HttpErrorDetailsExtractor httpErrorDetailsExtractor = new DefaultHttpErrorDetailsExtractor(); - - /** - * Set the error summary extractor. - *

By default, DefaultResponseErrorHandler uses a {@link DefaultHttpErrorDetailsExtractor}. - */ - public void setHttpErrorDetailsExtractor(HttpErrorDetailsExtractor httpErrorDetailsExtractor) { - Assert.notNull(httpErrorDetailsExtractor, "HttpErrorDetailsExtractor must not be null"); - this.httpErrorDetailsExtractor = httpErrorDetailsExtractor; - } - /** * Delegates to {@link #hasError(HttpStatus)} (for a standard status enum value) or * {@link #hasError(int)} (for an unknown status code) with the response status code. @@ -101,31 +93,58 @@ protected boolean hasError(int unknownStatusCode) { } /** - * Delegates to {@link #handleError(URI, HttpMethod, ClientHttpResponse, HttpStatus)} with the + * Delegates to {@link #handleError(ClientHttpResponse, HttpStatus)} with the * response status code. * @throws UnknownHttpStatusCodeException in case of an unresolvable status code - * @see #handleError(URI, HttpMethod, ClientHttpResponse, HttpStatus) + * @see #handleError(ClientHttpResponse, HttpStatus) */ @Override public void handleError(ClientHttpResponse response) throws IOException { - handleError(null, null, response); + HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode()); + if (statusCode == null) { + String message = getErrorMessage( + response.getRawStatusCode(), response.getStatusText(), + getResponseBody(response), getCharset(response)); + throw new UnknownHttpStatusCodeException(message, + response.getRawStatusCode(), response.getStatusText(), + response.getHeaders(), getResponseBody(response), getCharset(response)); + } + handleError(response, statusCode); } /** - * Delegates to {@link #handleError(URI, HttpMethod, ClientHttpResponse, HttpStatus)} with the - * response status code. - * @throws UnknownHttpStatusCodeException in case of an unresolvable status code - * @see #handleError(URI, HttpMethod, ClientHttpResponse, HttpStatus) + * Return error message with details from the response body, possibly truncated: + *

+	 * 404 Not Found: [{'id': 123, 'message': 'my very long... (500 bytes)]
+	 * 
*/ - @Override - public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { - HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode()); - if (statusCode == null) { - String message = httpErrorDetailsExtractor.getErrorDetails(response.getRawStatusCode(), response.getStatusText(), getResponseBody(response), getCharset(response), url, method); - throw new UnknownHttpStatusCodeException(message, response.getRawStatusCode(), response.getStatusText(), - response.getHeaders(), getResponseBody(response), getCharset(response), url, method); + private String getErrorMessage( + int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset) { + + String preface = rawStatusCode + " " + statusText + ": "; + if (ObjectUtils.isEmpty(responseBody)) { + return preface + "[no body]"; + } + + charset = charset == null ? StandardCharsets.UTF_8 : charset; + int maxChars = 200; + + if (responseBody.length < maxChars * 2) { + return preface + "[" + new String(responseBody, charset) + "]"; + } + + try { + Reader reader = new InputStreamReader(new ByteArrayInputStream(responseBody), charset); + CharBuffer buffer = CharBuffer.allocate(maxChars); + reader.read(buffer); + reader.close(); + buffer.flip(); + return preface + "[" + buffer.toString() + "... (" + responseBody.length + " bytes)]"; + } + catch (IOException ex) { + // should never happen + throw new IllegalStateException(ex); } - handleError(url, method, response, statusCode); } /** @@ -140,34 +159,19 @@ public void handleError(URI url, HttpMethod method, ClientHttpResponse response) * @see HttpServerErrorException#create */ protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException { - handleError(null, null, response, statusCode); - } - - /** - * Handle the error in the given response with the given resolved status code. - *

This default implementation throws a {@link HttpClientErrorException} if the response status code - * is {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR}, a {@link HttpServerErrorException} - * if it is {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR}, - * and a {@link RestClientException} in other cases. - * @since 5.0 - */ - protected void handleError(@Nullable URI url, @Nullable HttpMethod method, ClientHttpResponse response, - HttpStatus statusCode) throws IOException { - String statusText = response.getStatusText(); HttpHeaders headers = response.getHeaders(); byte[] body = getResponseBody(response); Charset charset = getCharset(response); - String message = httpErrorDetailsExtractor.getErrorDetails(statusCode.value(), statusText, body, charset, url, method); + String message = getErrorMessage(statusCode.value(), statusText, body, charset); switch (statusCode.series()) { case CLIENT_ERROR: - throw HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset, url, method); + throw HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset); case SERVER_ERROR: - throw HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset, url, method); + throw HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset); default: - throw new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, - charset, url, method); + throw new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset); } } 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 70c92265a3c6..ff6e3f83085a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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,11 +16,9 @@ package org.springframework.web.client; -import java.net.URI; import java.nio.charset.Charset; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; @@ -69,48 +67,84 @@ public HttpClientErrorException(HttpStatus statusCode, String statusText, } /** - * Constructor with a status code and status text, headers, content, request URL and method. + * Constructor with a status code and status text, headers, and content, + * and an prepared message. + * @since 5.2.2 */ public HttpClientErrorException(String message, HttpStatus statusCode, String statusText, - @Nullable HttpHeaders headers, @Nullable byte[] body, @Nullable Charset responseCharset, - @Nullable URI url, @Nullable HttpMethod method) { + @Nullable HttpHeaders headers, @Nullable byte[] body, @Nullable Charset responseCharset) { - super(message, statusCode, statusText, headers, body, responseCharset, url, method); + super(message, statusCode, statusText, headers, body, responseCharset); } + /** * Create {@code HttpClientErrorException} or an HTTP status specific sub-class. * @since 5.1 */ public static HttpClientErrorException create( - String message, HttpStatus statusCode, String statusText, HttpHeaders headers, byte[] body, - @Nullable Charset charset, @Nullable URI url, @Nullable HttpMethod method) { + HttpStatus statusCode, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + + return create(null, statusCode, statusText, headers, body, charset); + } + + /** + * Variant of {@link #create(HttpStatus, String, HttpHeaders, byte[], Charset)} + * with an optional prepared message. + * @since 5.2.2 + */ + public static HttpClientErrorException create(@Nullable String message, HttpStatus statusCode, + String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { switch (statusCode) { case BAD_REQUEST: - return new HttpClientErrorException.BadRequest(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.BadRequest(message, statusText, headers, body, charset) : + new HttpClientErrorException.BadRequest(statusText, headers, body, charset); case UNAUTHORIZED: - return new HttpClientErrorException.Unauthorized(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.Unauthorized(message, statusText, headers, body, charset) : + new HttpClientErrorException.Unauthorized(statusText, headers, body, charset); case FORBIDDEN: - return new HttpClientErrorException.Forbidden(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.Forbidden(message, statusText, headers, body, charset) : + new HttpClientErrorException.Forbidden(statusText, headers, body, charset); case NOT_FOUND: - return new HttpClientErrorException.NotFound(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.NotFound(message, statusText, headers, body, charset) : + new HttpClientErrorException.NotFound(statusText, headers, body, charset); case METHOD_NOT_ALLOWED: - return new HttpClientErrorException.MethodNotAllowed(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.MethodNotAllowed(message, statusText, headers, body, charset) : + new HttpClientErrorException.MethodNotAllowed(statusText, headers, body, charset); case NOT_ACCEPTABLE: - return new HttpClientErrorException.NotAcceptable(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.NotAcceptable(message, statusText, headers, body, charset) : + new HttpClientErrorException.NotAcceptable(statusText, headers, body, charset); case CONFLICT: - return new HttpClientErrorException.Conflict(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.Conflict(message, statusText, headers, body, charset) : + new HttpClientErrorException.Conflict(statusText, headers, body, charset); case GONE: - return new HttpClientErrorException.Gone(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.Gone(message, statusText, headers, body, charset) : + new HttpClientErrorException.Gone(statusText, headers, body, charset); case UNSUPPORTED_MEDIA_TYPE: - return new HttpClientErrorException.UnsupportedMediaType(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.UnsupportedMediaType(message, statusText, headers, body, charset) : + new HttpClientErrorException.UnsupportedMediaType(statusText, headers, body, charset); case TOO_MANY_REQUESTS: - return new HttpClientErrorException.TooManyRequests(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.TooManyRequests(message, statusText, headers, body, charset) : + new HttpClientErrorException.TooManyRequests(statusText, headers, body, charset); case UNPROCESSABLE_ENTITY: - return new HttpClientErrorException.UnprocessableEntity(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException.UnprocessableEntity(message, statusText, headers, body, charset) : + new HttpClientErrorException.UnprocessableEntity(statusText, headers, body, charset); default: - return new HttpClientErrorException(message, statusCode, statusText, headers, body, charset, url, method); + return message != null ? + new HttpClientErrorException(message, statusCode, statusText, headers, body, charset) : + new HttpClientErrorException(statusCode, statusText, headers, body, charset); } } @@ -122,12 +156,16 @@ public static HttpClientErrorException create( * @since 5.1 */ @SuppressWarnings("serial") - public static class BadRequest extends HttpClientErrorException { + public static final class BadRequest extends HttpClientErrorException { - BadRequest(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private BadRequest(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.BAD_REQUEST, statusText, headers, body, charset); + } + + private BadRequest(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.BAD_REQUEST, statusText, headers, body, charset, url, method); + super(message, HttpStatus.BAD_REQUEST, statusText, headers, body, charset); } } @@ -136,12 +174,16 @@ public static class BadRequest extends HttpClientErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class Unauthorized extends HttpClientErrorException { + public static final class Unauthorized extends HttpClientErrorException { + + private Unauthorized(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.UNAUTHORIZED, statusText, headers, body, charset); + } - Unauthorized(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private Unauthorized(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.UNAUTHORIZED, statusText, headers, body, charset, url, method); + super(message, HttpStatus.UNAUTHORIZED, statusText, headers, body, charset); } } @@ -150,12 +192,16 @@ public static class Unauthorized extends HttpClientErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class Forbidden extends HttpClientErrorException { + public static final class Forbidden extends HttpClientErrorException { - Forbidden(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private Forbidden(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.FORBIDDEN, statusText, headers, body, charset); + } + + private Forbidden(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.FORBIDDEN, statusText, headers, body, charset, url, method); + super(message, HttpStatus.FORBIDDEN, statusText, headers, body, charset); } } @@ -164,12 +210,16 @@ public static class Forbidden extends HttpClientErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class NotFound extends HttpClientErrorException { + public static final class NotFound extends HttpClientErrorException { - NotFound(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private NotFound(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.NOT_FOUND, statusText, headers, body, charset); + } - super(message, HttpStatus.NOT_FOUND, statusText, headers, body, charset, url, method); + private NotFound(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { + + super(message, HttpStatus.NOT_FOUND, statusText, headers, body, charset); } } @@ -178,12 +228,16 @@ public static class NotFound extends HttpClientErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class MethodNotAllowed extends HttpClientErrorException { + public static final class MethodNotAllowed extends HttpClientErrorException { + + private MethodNotAllowed(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.METHOD_NOT_ALLOWED, statusText, headers, body, charset); + } - MethodNotAllowed(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private MethodNotAllowed(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.METHOD_NOT_ALLOWED, statusText, headers, body, charset, url, method); + super(message, HttpStatus.METHOD_NOT_ALLOWED, statusText, headers, body, charset); } } @@ -192,12 +246,16 @@ public static class MethodNotAllowed extends HttpClientErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class NotAcceptable extends HttpClientErrorException { + public static final class NotAcceptable extends HttpClientErrorException { - NotAcceptable(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private NotAcceptable(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.NOT_ACCEPTABLE, statusText, headers, body, charset); + } - super(message, HttpStatus.NOT_ACCEPTABLE, statusText, headers, body, charset, url, method); + private NotAcceptable(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { + + super(message, HttpStatus.NOT_ACCEPTABLE, statusText, headers, body, charset); } } @@ -206,12 +264,14 @@ public static class NotAcceptable extends HttpClientErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class Conflict extends HttpClientErrorException { + public static final class Conflict extends HttpClientErrorException { - Conflict(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private Conflict(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.CONFLICT, statusText, headers, body, charset); + } - super(message, HttpStatus.CONFLICT, statusText, headers, body, charset, url, method); + private Conflict(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(message, HttpStatus.CONFLICT, statusText, headers, body, charset); } } @@ -220,12 +280,14 @@ public static class Conflict extends HttpClientErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class Gone extends HttpClientErrorException { + public static final class Gone extends HttpClientErrorException { - Gone(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private Gone(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.GONE, statusText, headers, body, charset); + } - super(message, HttpStatus.GONE, statusText, headers, body, charset, url, method); + private Gone(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(message, HttpStatus.GONE, statusText, headers, body, charset); } } @@ -234,12 +296,16 @@ public static class Gone extends HttpClientErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class UnsupportedMediaType extends HttpClientErrorException { + public static final class UnsupportedMediaType extends HttpClientErrorException { - UnsupportedMediaType(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private UnsupportedMediaType(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.UNSUPPORTED_MEDIA_TYPE, statusText, headers, body, charset); + } + + private UnsupportedMediaType(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.UNSUPPORTED_MEDIA_TYPE, statusText, headers, body, charset, url, method); + super(message, HttpStatus.UNSUPPORTED_MEDIA_TYPE, statusText, headers, body, charset); } } @@ -248,12 +314,16 @@ public static class UnsupportedMediaType extends HttpClientErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class UnprocessableEntity extends HttpClientErrorException { + public static final class UnprocessableEntity extends HttpClientErrorException { + + private UnprocessableEntity(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.UNPROCESSABLE_ENTITY, statusText, headers, body, charset); + } - UnprocessableEntity(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private UnprocessableEntity(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.UNPROCESSABLE_ENTITY, statusText, headers, body, charset, url, method); + super(message, HttpStatus.UNPROCESSABLE_ENTITY, statusText, headers, body, charset); } } @@ -262,12 +332,16 @@ public static class UnprocessableEntity extends HttpClientErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class TooManyRequests extends HttpClientErrorException { + public static final class TooManyRequests extends HttpClientErrorException { + + private TooManyRequests(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.TOO_MANY_REQUESTS, statusText, headers, body, charset); + } - TooManyRequests(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private TooManyRequests(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.TOO_MANY_REQUESTS, statusText, headers, body, charset, url, method); + super(message, HttpStatus.TOO_MANY_REQUESTS, statusText, headers, body, charset); } } diff --git a/spring-web/src/main/java/org/springframework/web/client/HttpErrorDetailsExtractor.java b/spring-web/src/main/java/org/springframework/web/client/HttpErrorDetailsExtractor.java deleted file mode 100644 index d141549c8569..000000000000 --- a/spring-web/src/main/java/org/springframework/web/client/HttpErrorDetailsExtractor.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.springframework.web.client; - -import java.net.URI; -import java.nio.charset.Charset; - -import org.jetbrains.annotations.NotNull; -import org.springframework.http.HttpMethod; -import org.springframework.lang.Nullable; - -/** - * Strategy interface used by the {@link DefaultResponseErrorHandler} to compose - * a summary of the http error. - * - * @author Jerzy Krolak - * @since 5.1 - */ -public interface HttpErrorDetailsExtractor { - - /** - * Assemble HTTP error response details string, based on the provided response details. - * @param rawStatusCode HTTP status code - * @param statusText HTTP status text - * @param responseBody response body - * @param responseCharset response charset - * @param url request URI - * @param method request method - * @return error details string - */ - @NotNull - String getErrorDetails(int rawStatusCode, String statusText, @Nullable byte[] responseBody, - @Nullable Charset responseCharset, @Nullable URI url, @Nullable HttpMethod method); - -} 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 4a63be5cf160..d45eb16da9ce 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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,11 +16,9 @@ package org.springframework.web.client; -import java.net.URI; import java.nio.charset.Charset; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; @@ -69,35 +67,59 @@ public HttpServerErrorException(HttpStatus statusCode, String statusText, } /** - * Constructor with a status code and status text, headers, content, request URL, and method. + * Constructor with a status code and status text, headers, content, and an + * prepared message. + * @since 5.2.2 */ public HttpServerErrorException(String message, HttpStatus statusCode, String statusText, - @Nullable HttpHeaders headers, @Nullable byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { - super(message, statusCode, statusText, headers, body, charset, url, method); + @Nullable HttpHeaders headers, @Nullable byte[] body, @Nullable Charset charset) { + + super(message, statusCode, statusText, headers, body, charset); } /** * Create an {@code HttpServerErrorException} or an HTTP status specific sub-class. * @since 5.1 */ - public static HttpServerErrorException create( - String message, HttpStatus statusCode, String statusText, HttpHeaders headers, byte[] body, - @Nullable Charset charset, @Nullable URI url, @Nullable HttpMethod method) { + public static HttpServerErrorException create(HttpStatus statusCode, + String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + + return create(null, statusCode, statusText, headers, body, charset); + } + + /** + * Variant of {@link #create(String, HttpStatus, String, HttpHeaders, byte[], Charset)} + * with an optional prepared message. + * @since 5.2.2. + */ + public static HttpServerErrorException create(@Nullable String message, HttpStatus statusCode, + String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { switch (statusCode) { case INTERNAL_SERVER_ERROR: - return new HttpServerErrorException.InternalServerError(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpServerErrorException.InternalServerError(message, statusText, headers, body, charset) : + new HttpServerErrorException.InternalServerError(statusText, headers, body, charset); case NOT_IMPLEMENTED: - return new HttpServerErrorException.NotImplemented(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpServerErrorException.NotImplemented(message, statusText, headers, body, charset) : + new HttpServerErrorException.NotImplemented(statusText, headers, body, charset); case BAD_GATEWAY: - return new HttpServerErrorException.BadGateway(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpServerErrorException.BadGateway(message, statusText, headers, body, charset) : + new HttpServerErrorException.BadGateway(statusText, headers, body, charset); case SERVICE_UNAVAILABLE: - return new HttpServerErrorException.ServiceUnavailable(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpServerErrorException.ServiceUnavailable(message, statusText, headers, body, charset) : + new HttpServerErrorException.ServiceUnavailable(statusText, headers, body, charset); case GATEWAY_TIMEOUT: - return new HttpServerErrorException.GatewayTimeout(message, statusText, headers, body, charset, url, method); + return message != null ? + new HttpServerErrorException.GatewayTimeout(message, statusText, headers, body, charset) : + new HttpServerErrorException.GatewayTimeout(statusText, headers, body, charset); default: - return new HttpServerErrorException(message, statusCode, statusText, headers, body, charset, url, method); + return message != null ? + new HttpServerErrorException(message, statusCode, statusText, headers, body, charset) : + new HttpServerErrorException(statusCode, statusText, headers, body, charset); } } @@ -109,12 +131,16 @@ public static HttpServerErrorException create( * @since 5.1 */ @SuppressWarnings("serial") - public static class InternalServerError extends HttpServerErrorException { + public static final class InternalServerError extends HttpServerErrorException { + + private InternalServerError(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.INTERNAL_SERVER_ERROR, statusText, headers, body, charset); + } - InternalServerError(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private InternalServerError(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.INTERNAL_SERVER_ERROR, statusText, headers, body, charset, url, method); + super(message, HttpStatus.INTERNAL_SERVER_ERROR, statusText, headers, body, charset); } } @@ -123,12 +149,16 @@ public static class InternalServerError extends HttpServerErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class NotImplemented extends HttpServerErrorException { + public static final class NotImplemented extends HttpServerErrorException { - NotImplemented(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private NotImplemented(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.NOT_IMPLEMENTED, statusText, headers, body, charset); + } + + private NotImplemented(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.NOT_IMPLEMENTED, statusText, headers, body, charset, url, method); + super(message, HttpStatus.NOT_IMPLEMENTED, statusText, headers, body, charset); } } @@ -137,12 +167,16 @@ public static class NotImplemented extends HttpServerErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class BadGateway extends HttpServerErrorException { + public static final class BadGateway extends HttpServerErrorException { + + private BadGateway(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.BAD_GATEWAY, statusText, headers, body, charset); + } - BadGateway(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private BadGateway(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.BAD_GATEWAY, statusText, headers, body, charset, url, method); + super(message, HttpStatus.BAD_GATEWAY, statusText, headers, body, charset); } } @@ -151,12 +185,16 @@ public static class BadGateway extends HttpServerErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class ServiceUnavailable extends HttpServerErrorException { + public static final class ServiceUnavailable extends HttpServerErrorException { - ServiceUnavailable(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private ServiceUnavailable(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.SERVICE_UNAVAILABLE, statusText, headers, body, charset); + } + + private ServiceUnavailable(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.SERVICE_UNAVAILABLE, statusText, headers, body, charset, url, method); + super(message, HttpStatus.SERVICE_UNAVAILABLE, statusText, headers, body, charset); } } @@ -165,12 +203,16 @@ public static class ServiceUnavailable extends HttpServerErrorException { * @since 5.1 */ @SuppressWarnings("serial") - public static class GatewayTimeout extends HttpServerErrorException { + public static final class GatewayTimeout extends HttpServerErrorException { + + private GatewayTimeout(String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset) { + super(HttpStatus.GATEWAY_TIMEOUT, statusText, headers, body, charset); + } - GatewayTimeout(String message, String statusText, HttpHeaders headers, byte[] body, @Nullable Charset charset, - @Nullable URI url, @Nullable HttpMethod method) { + private GatewayTimeout(String message, String statusText, + HttpHeaders headers, byte[] body, @Nullable Charset charset) { - super(message, HttpStatus.GATEWAY_TIMEOUT, statusText, headers, body, charset, url, method); + super(message, HttpStatus.GATEWAY_TIMEOUT, statusText, headers, body, charset); } } 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 726e77558737..b660f15be870 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 @@ -16,11 +16,9 @@ package org.springframework.web.client; -import java.net.URI; import java.nio.charset.Charset; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; @@ -85,8 +83,8 @@ protected HttpStatusCodeException(HttpStatus statusCode, String statusText, protected HttpStatusCodeException(HttpStatus statusCode, String statusText, @Nullable HttpHeaders responseHeaders, @Nullable byte[] responseBody, @Nullable Charset responseCharset) { - this(getMessage(statusCode, statusText), statusCode, statusText, - responseHeaders, responseBody, responseCharset, null, null); + this(getMessage(statusCode, statusText), + statusCode, statusText, responseHeaders, responseBody, responseCharset); } /** @@ -98,15 +96,12 @@ protected HttpStatusCodeException(HttpStatus statusCode, String statusText, * @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} - * @param url the request URL, may be {@code null} - * @param method the request method, may be {@code null} + * @since 5.2.2 */ protected HttpStatusCodeException(String message, HttpStatus statusCode, String statusText, - @Nullable HttpHeaders responseHeaders, @Nullable byte[] responseBody, @Nullable Charset responseCharset, - @Nullable URI url, @Nullable HttpMethod method) { + @Nullable HttpHeaders responseHeaders, @Nullable byte[] responseBody, @Nullable Charset responseCharset) { - super(message, statusCode.value(), statusText, responseHeaders, responseBody, - responseCharset, url, method); + super(message, statusCode.value(), statusText, responseHeaders, responseBody, responseCharset); this.statusCode = statusCode; } diff --git a/spring-web/src/main/java/org/springframework/web/client/RestClientResponseException.java b/spring-web/src/main/java/org/springframework/web/client/RestClientResponseException.java index 5410096477a4..2a927f376905 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestClientResponseException.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestClientResponseException.java @@ -17,12 +17,10 @@ package org.springframework.web.client; import java.io.UnsupportedEncodingException; -import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.lang.Nullable; /** @@ -50,12 +48,6 @@ public class RestClientResponseException extends RestClientException { @Nullable private final String responseCharset; - @Nullable - private final HttpMethod method; - - @Nullable - private final URI url; - /** * Construct a new instance of with the given response data. @@ -67,22 +59,6 @@ public class RestClientResponseException extends RestClientException { */ public RestClientResponseException(String message, int statusCode, String statusText, @Nullable HttpHeaders responseHeaders, @Nullable byte[] responseBody, @Nullable Charset responseCharset) { - this(message, statusCode, statusText, responseHeaders, responseBody, responseCharset, null, null); - } - - /** - * Construct a new instance of with the given response data. - * @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}) - * @param responseCharset the response body charset (may be {@code null}) - * @param url the request URL (may be {@code null}) - * @param method the request method (may be {@code null}) - */ - public RestClientResponseException(String message, int statusCode, String statusText, - @Nullable HttpHeaders responseHeaders, @Nullable byte[] responseBody, @Nullable Charset responseCharset, - @Nullable URI url, @Nullable HttpMethod method) { super(message); this.rawStatusCode = statusCode; @@ -90,8 +66,6 @@ public RestClientResponseException(String message, int statusCode, String status this.responseHeaders = responseHeaders; this.responseBody = (responseBody != null ? responseBody : new byte[0]); this.responseCharset = (responseCharset != null ? responseCharset.name() : null); - this.method = method; - this.url = url; } @@ -124,20 +98,6 @@ public byte[] getResponseBodyAsByteArray() { return this.responseBody; } - /** - * Return the HTTP request method. - */ - public HttpMethod getMethod() { - return method; - } - - /** - * Return the HTTP request url. - */ - public URI getUrl() { - return url; - } - /** * Return the response body converted to String. The charset used is that * of the response "Content-Type" or otherwise {@code "UTF-8"}. diff --git a/spring-web/src/main/java/org/springframework/web/client/UnknownHttpStatusCodeException.java b/spring-web/src/main/java/org/springframework/web/client/UnknownHttpStatusCodeException.java index 79d5544ad022..c7b0319018d4 100644 --- a/spring-web/src/main/java/org/springframework/web/client/UnknownHttpStatusCodeException.java +++ b/spring-web/src/main/java/org/springframework/web/client/UnknownHttpStatusCodeException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 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,11 +16,9 @@ package org.springframework.web.client; -import java.net.URI; import java.nio.charset.Charset; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; @@ -48,7 +46,7 @@ public UnknownHttpStatusCodeException(int rawStatusCode, String statusText, @Nul @Nullable byte[] responseBody, @Nullable Charset responseCharset) { this("Unknown status code [" + rawStatusCode + "]" + " " + statusText, - rawStatusCode, statusText, responseHeaders, responseBody, responseCharset, null, null); + rawStatusCode, statusText, responseHeaders, responseBody, responseCharset); } /** @@ -59,12 +57,11 @@ public UnknownHttpStatusCodeException(int rawStatusCode, String statusText, @Nul * @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}) - * @param url the request URI (may be {@code null}) - * @param method the request HTTP method (may be {@code null}) + * @since 5.2.2 */ - public UnknownHttpStatusCodeException(String message, int rawStatusCode, String statusText, @Nullable HttpHeaders responseHeaders, - @Nullable byte[] responseBody, @Nullable Charset responseCharset, @Nullable URI url, @Nullable HttpMethod method) { + public UnknownHttpStatusCodeException(String message, int rawStatusCode, String statusText, + @Nullable HttpHeaders responseHeaders, @Nullable byte[] responseBody, @Nullable Charset responseCharset) { - super(message, rawStatusCode, statusText, responseHeaders, responseBody, responseCharset, url, method); + super(message, rawStatusCode, statusText, responseHeaders, responseBody, responseCharset); } } diff --git a/spring-web/src/test/java/org/springframework/web/client/DefaultHttpErrorDetailsExtractorTests.java b/spring-web/src/test/java/org/springframework/web/client/DefaultHttpErrorDetailsExtractorTests.java deleted file mode 100644 index 94804ac69f8a..000000000000 --- a/spring-web/src/test/java/org/springframework/web/client/DefaultHttpErrorDetailsExtractorTests.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.springframework.web.client; - -import java.net.URI; - -import com.google.common.base.Strings; -import org.junit.Test; - -import static java.nio.charset.StandardCharsets.*; -import static org.junit.Assert.*; -import static org.springframework.http.HttpMethod.*; -import static org.springframework.http.HttpStatus.*; - -public class DefaultHttpErrorDetailsExtractorTests { - - private final DefaultHttpErrorDetailsExtractor extractor = new DefaultHttpErrorDetailsExtractor(); - - @Test - public void shouldGetSimpleExceptionMessage() { - String actual = extractor.getErrorDetails(NOT_FOUND.value(), "Not Found", null, null, null, null); - - assertEquals("Should get a simple message", "404 Not Found", actual); - } - - @Test - public void shouldGetCompleteMessageWithoutBody() { - String actual = extractor.getErrorDetails(NOT_FOUND.value(), "Not Found", null, null, URI.create("http://localhost:8080/my-endpoint"), GET); - - assertEquals("Should get a complete message without body", "404 Not Found after GET http://localhost:8080/my-endpoint : [no body]", actual); - } - - @Test - public void shouldGetCompleteMessageWithShortAsciiBodyNoCharset() { - String responseBody = "my short response body"; - - String actual = extractor.getErrorDetails(NOT_FOUND.value(), "Not Found", responseBody.getBytes(), null, URI.create("http://localhost:8080/my-endpoint"), GET); - - assertEquals("Should get a simple message", "404 Not Found after GET http://localhost:8080/my-endpoint : [my short response body]", actual); - } - - @Test - public void shouldGetCompleteMessageWithShortAsciiBodyUtfCharset() { - String responseBody = "my short response body"; - - String actual = extractor.getErrorDetails(NOT_FOUND.value(), "Not Found", responseBody.getBytes(), UTF_8, URI.create("http://localhost:8080/my-endpoint"), GET); - - assertEquals("Should get a simple message", "404 Not Found after GET http://localhost:8080/my-endpoint : [my short response body]", actual); - } - - @Test - public void shouldGetCompleteMessageWithShortUtfBodyUtfCharset() { - String responseBody = "my short response body \u0105\u0119"; - - String actual = extractor.getErrorDetails(NOT_FOUND.value(), "Not Found", responseBody.getBytes(), UTF_8, URI.create("http://localhost:8080/my-endpoint"), GET); - - assertEquals("Should get a simple message", "404 Not Found after GET http://localhost:8080/my-endpoint : [my short response body \u0105\u0119]", actual); - } - - @Test - public void shouldGetCompleteMessageWithShortUtfBodyNoCharset() { - String responseBody = "my short response body \u0105\u0119"; - - String actual = extractor.getErrorDetails(NOT_FOUND.value(), "Not Found", responseBody.getBytes(UTF_8), null, URI.create("http://localhost:8080/my-endpoint"), GET); - - assertEquals("Should get a simple message", "404 Not Found after GET http://localhost:8080/my-endpoint : [my short response body \u00c4\u0085\u00c4\u0099]", actual); - } - - @Test - public void shouldGetCompleteMessageWithLongAsciiBodyNoCharset() { - String responseBody = Strings.repeat("asdfg", 100); - String expectedMessage = "404 Not Found after GET http://localhost:8080/my-endpoint : [" + Strings.repeat("asdfg", 40) + "... (500 bytes)]"; - - String actual = extractor.getErrorDetails(NOT_FOUND.value(), "Not Found", responseBody.getBytes(UTF_8), null, URI.create("http://localhost:8080/my-endpoint"), GET); - - assertEquals("Should get a simple message", expectedMessage, actual); - } - - @Test - public void shouldGetCompleteMessageWithLongUtfBodyNoCharset() { - String responseBody = Strings.repeat("asd\u0105\u0119", 100); - String expectedMessage = "404 Not Found after GET http://localhost:8080/my-endpoint : [" + Strings.repeat("asd\u00c4\u0085\u00c4\u0099", 28) + "asd\u00c4... (700 bytes)]"; - - String actual = extractor.getErrorDetails(NOT_FOUND.value(), "Not Found", responseBody.getBytes(UTF_8), null, URI.create("http://localhost:8080/my-endpoint"), GET); - - assertEquals("Should get a simple message", expectedMessage, actual); - } - - @Test - public void shouldGetCompleteMessageWithLongUtfBodyUtfCharset() { - String responseBody = Strings.repeat("asd\u0105\u0119", 100); - String expectedMessage = "404 Not Found after GET http://localhost:8080/my-endpoint : [" + Strings.repeat("asd\u0105\u0119", 40) + "... (700 bytes)]"; - - String actual = extractor.getErrorDetails(NOT_FOUND.value(), "Not Found", responseBody.getBytes(UTF_8), UTF_8, URI.create("http://localhost:8080/my-endpoint"), GET); - - assertEquals("Should get a simple message", expectedMessage, actual); - } - -} \ No newline at end of file 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 e9f782609378..c5c05c48e426 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 @@ -19,8 +19,10 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.function.Function; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -69,9 +71,28 @@ public void handleError() throws Exception { given(response.getHeaders()).willReturn(headers); given(response.getBody()).willReturn(new ByteArrayInputStream("Hello World".getBytes(StandardCharsets.UTF_8))); - assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> - handler.handleError(response)) - .satisfies(ex -> assertThat(ex.getResponseHeaders()).isSameAs(headers)); + assertThatExceptionOfType(HttpClientErrorException.class) + .isThrownBy(() -> handler.handleError(response)) + .satisfies(ex -> assertThat(ex.getResponseHeaders()).isSameAs(headers)) + .satisfies(ex -> assertThat(ex.getMessage()).isEqualTo("404 Not Found: [Hello World]")); + } + + @Test + public void handleErrorWithLongBody() throws Exception { + + Function bodyGenerator = + size -> Flux.just("a").repeat(size-1).reduce((s, s2) -> s + s2).block(); + + given(response.getRawStatusCode()).willReturn(HttpStatus.NOT_FOUND.value()); + given(response.getStatusText()).willReturn("Not Found"); + given(response.getHeaders()).willReturn(new HttpHeaders()); + given(response.getBody()).willReturn( + new ByteArrayInputStream(bodyGenerator.apply(500).getBytes(StandardCharsets.UTF_8))); + + assertThatExceptionOfType(HttpClientErrorException.class) + .isThrownBy(() -> handler.handleError(response)) + .satisfies(ex -> assertThat(ex.getMessage()).isEqualTo( + "404 Not Found: [" + bodyGenerator.apply(200) + "... (500 bytes)]")); } @Test @@ -84,8 +105,7 @@ public void handleErrorIOException() throws Exception { given(response.getHeaders()).willReturn(headers); given(response.getBody()).willThrow(new IOException()); - assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> - handler.handleError(response)); + assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> handler.handleError(response)); } @Test diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java index 631725b6bd29..f53cf890c79b 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java @@ -254,8 +254,7 @@ void badRequest(ClientHttpRequestFactory clientHttpRequestFactory) { template.execute(baseUrl + "/status/badrequest", HttpMethod.GET, null, null)) .satisfies(ex -> { assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); - assertThat(ex.getMessage()).isEqualTo( - "400 Client Error after GET http://localhost:" + port + "/status/badrequest : [no body]"); + assertThat(ex.getMessage()).isEqualTo("400 Client Error: [no body]"); }); }