Polishing in DefaultResponseErrorHandler
See gh-33980
This commit is contained in:
@@ -38,7 +38,6 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -135,8 +134,7 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
|
||||
*/
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse response) throws IOException {
|
||||
HttpStatusCode statusCode = response.getStatusCode();
|
||||
handleError(response, statusCode, null, null);
|
||||
handleError(response, response.getStatusCode(), null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,17 +157,78 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
|
||||
*/
|
||||
@Override
|
||||
public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
|
||||
HttpStatusCode statusCode = response.getStatusCode();
|
||||
handleError(response, statusCode, url, method);
|
||||
handleError(response, response.getStatusCode(), url, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return error message with details from the response body. For example:
|
||||
* Handle the error based on the resolved status code.
|
||||
* <p>The default implementation delegates to
|
||||
* {@link HttpClientErrorException#create} for errors in the 4xx range, to
|
||||
* {@link HttpServerErrorException#create} for errors in the 5xx range,
|
||||
* or otherwise raises {@link UnknownHttpStatusCodeException}.
|
||||
* @since 6.2
|
||||
* @see HttpClientErrorException#create
|
||||
* @see HttpServerErrorException#create
|
||||
*/
|
||||
protected void handleError(
|
||||
ClientHttpResponse response, HttpStatusCode statusCode,
|
||||
@Nullable URI url, @Nullable HttpMethod method) throws IOException {
|
||||
|
||||
String statusText = response.getStatusText();
|
||||
HttpHeaders headers = response.getHeaders();
|
||||
byte[] body = getResponseBody(response);
|
||||
Charset charset = getCharset(response);
|
||||
String message = getErrorMessage(statusCode.value(), statusText, body, charset, url, method);
|
||||
|
||||
RestClientResponseException ex;
|
||||
if (statusCode.is4xxClientError()) {
|
||||
ex = HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset);
|
||||
}
|
||||
else if (statusCode.is5xxServerError()) {
|
||||
ex = HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset);
|
||||
}
|
||||
else {
|
||||
ex = new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset);
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(this.messageConverters)) {
|
||||
ex.setBodyConvertFunction(initBodyConvertFunction(response, body));
|
||||
}
|
||||
|
||||
throw ex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the body of the given response (for inclusion in a status exception).
|
||||
* @param response the response to inspect
|
||||
* @return the response body as a byte array,
|
||||
* or an empty byte array if the body could not be read
|
||||
* @since 4.3.8
|
||||
*/
|
||||
protected byte[] getResponseBody(ClientHttpResponse response) {
|
||||
return RestClientUtils.getBody(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the charset of the response (for inclusion in a status exception).
|
||||
* @param response the response to inspect
|
||||
* @return the associated charset, or {@code null} if none
|
||||
* @since 4.3.8
|
||||
*/
|
||||
@Nullable
|
||||
protected Charset getCharset(ClientHttpResponse response) {
|
||||
MediaType contentType = response.getHeaders().getContentType();
|
||||
return (contentType != null ? contentType.getCharset() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an error message with details from the response body. For example:
|
||||
* <pre>
|
||||
* 404 Not Found on GET request for "https://example.com": [{'id': 123, 'message': 'my message'}]
|
||||
* </pre>
|
||||
*/
|
||||
private String getErrorMessage(int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset,
|
||||
private String getErrorMessage(
|
||||
int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset,
|
||||
@Nullable URI url, @Nullable HttpMethod method) {
|
||||
|
||||
StringBuilder msg = new StringBuilder(rawStatusCode + " " + statusText);
|
||||
@@ -201,43 +260,6 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
|
||||
return msg.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the error based on the resolved status code.
|
||||
* <p>The default implementation delegates to
|
||||
* {@link HttpClientErrorException#create} for errors in the 4xx range, to
|
||||
* {@link HttpServerErrorException#create} for errors in the 5xx range,
|
||||
* or otherwise raises {@link UnknownHttpStatusCodeException}.
|
||||
* @since 6.2
|
||||
* @see HttpClientErrorException#create
|
||||
* @see HttpServerErrorException#create
|
||||
*/
|
||||
protected void handleError(ClientHttpResponse response, HttpStatusCode statusCode,
|
||||
@Nullable URI url, @Nullable HttpMethod method) throws IOException {
|
||||
|
||||
String statusText = response.getStatusText();
|
||||
HttpHeaders headers = response.getHeaders();
|
||||
byte[] body = getResponseBody(response);
|
||||
Charset charset = getCharset(response);
|
||||
String message = getErrorMessage(statusCode.value(), statusText, body, charset, url, method);
|
||||
|
||||
RestClientResponseException ex;
|
||||
if (statusCode.is4xxClientError()) {
|
||||
ex = HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset);
|
||||
}
|
||||
else if (statusCode.is5xxServerError()) {
|
||||
ex = HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset);
|
||||
}
|
||||
else {
|
||||
ex = new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset);
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(this.messageConverters)) {
|
||||
ex.setBodyConvertFunction(initBodyConvertFunction(response, body));
|
||||
}
|
||||
|
||||
throw ex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a function for decoding the error content. This can be passed to
|
||||
* {@link RestClientResponseException#setBodyConvertFunction(Function)}.
|
||||
@@ -265,34 +287,4 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the body of the given response (for inclusion in a status exception).
|
||||
* @param response the response to inspect
|
||||
* @return the response body as a byte array,
|
||||
* or an empty byte array if the body could not be read
|
||||
* @since 4.3.8
|
||||
*/
|
||||
protected byte[] getResponseBody(ClientHttpResponse response) {
|
||||
try {
|
||||
return FileCopyUtils.copyToByteArray(response.getBody());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the charset of the response (for inclusion in a status exception).
|
||||
* @param response the response to inspect
|
||||
* @return the associated charset, or {@code null} if none
|
||||
* @since 4.3.8
|
||||
*/
|
||||
@Nullable
|
||||
protected Charset getCharset(ClientHttpResponse response) {
|
||||
HttpHeaders headers = response.getHeaders();
|
||||
MediaType contentType = headers.getContentType();
|
||||
return (contentType != null ? contentType.getCharset() : null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,26 +32,27 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ResponseErrorHandler} that uses {@link HttpMessageConverter
|
||||
* HttpMessageConverters} to convert HTTP error responses to {@link RestClientException
|
||||
* RestClientExceptions}.
|
||||
* Implementation of {@link ResponseErrorHandler} that uses
|
||||
* {@link HttpMessageConverter HttpMessageConverters} to convert HTTP error
|
||||
* responses to {@link RestClientException RestClientExceptions}.
|
||||
*
|
||||
* <p>To use this error handler, you must specify a
|
||||
* {@linkplain #setStatusMapping(Map) status mapping} and/or a
|
||||
* {@linkplain #setSeriesMapping(Map) series mapping}. If either of these mappings has a match
|
||||
* for the {@linkplain ClientHttpResponse#getStatusCode() status code} of a given
|
||||
* {@code ClientHttpResponse}, {@link #hasError(ClientHttpResponse)} will return
|
||||
* {@code true}, and {@link #handleError(ClientHttpResponse)} will attempt to use the
|
||||
* {@linkplain #setMessageConverters(List) configured message converters} to convert the response
|
||||
* into the mapped subclass of {@link RestClientException}. Note that the
|
||||
* {@linkplain #setStatusMapping(Map) status mapping} takes precedence over
|
||||
* {@linkplain #setSeriesMapping(Map) series mapping}.
|
||||
* {@linkplain #setSeriesMapping(Map) series mapping}. If either of these
|
||||
* mappings has a match for the {@linkplain ClientHttpResponse#getStatusCode()
|
||||
* status code} of a given {@code ClientHttpResponse},
|
||||
* {@link #hasError(ClientHttpResponse)} will return {@code true}, and
|
||||
* {@link #handleError(ClientHttpResponse)} will attempt to use the
|
||||
* {@linkplain #setMessageConverters(List) configured message converters} to
|
||||
* convert the response into the mapped subclass of {@link RestClientException}.
|
||||
* Note that the {@linkplain #setStatusMapping(Map) status mapping} takes
|
||||
* precedence over {@linkplain #setSeriesMapping(Map) series mapping}.
|
||||
*
|
||||
* <p>If there is no match, this error handler will default to the behavior of
|
||||
* {@link DefaultResponseErrorHandler}. Note that you can override this default behavior
|
||||
* by specifying a {@linkplain #setSeriesMapping(Map) series mapping} from
|
||||
* {@code HttpStatus.Series#CLIENT_ERROR} and/or {@code HttpStatus.Series#SERVER_ERROR}
|
||||
* to {@code null}.
|
||||
* {@link DefaultResponseErrorHandler}. Note that you can override this default
|
||||
* behavior by specifying a {@linkplain #setSeriesMapping(Map) series mapping}
|
||||
* from {@code HttpStatus.Series#CLIENT_ERROR} and/or
|
||||
* {@code HttpStatus.Series#SERVER_ERROR} to {@code null}.
|
||||
*
|
||||
* @author Simon Galperin
|
||||
* @author Arjen Poutsma
|
||||
@@ -126,11 +127,11 @@ public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler
|
||||
@Override
|
||||
protected boolean hasError(HttpStatusCode statusCode) {
|
||||
if (this.statusMapping.containsKey(statusCode)) {
|
||||
return this.statusMapping.get(statusCode) != null;
|
||||
return (this.statusMapping.get(statusCode) != null);
|
||||
}
|
||||
HttpStatus.Series series = HttpStatus.Series.resolve(statusCode.value());
|
||||
if (this.seriesMapping.containsKey(series)) {
|
||||
return this.seriesMapping.get(series) != null;
|
||||
return (this.seriesMapping.get(series) != null);
|
||||
}
|
||||
else {
|
||||
return super.hasError(statusCode);
|
||||
@@ -138,15 +139,14 @@ public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
|
||||
handleError(response, response.getStatusCode(), url, method);
|
||||
}
|
||||
protected void handleError(
|
||||
ClientHttpResponse response, HttpStatusCode statusCode,
|
||||
@Nullable URI url, @Nullable HttpMethod method) throws IOException {
|
||||
|
||||
@Override
|
||||
protected void handleError(ClientHttpResponse response, HttpStatusCode statusCode, @Nullable URI url, @Nullable HttpMethod method) throws IOException {
|
||||
if (this.statusMapping.containsKey(statusCode)) {
|
||||
extract(this.statusMapping.get(statusCode), response);
|
||||
}
|
||||
|
||||
HttpStatus.Series series = HttpStatus.Series.resolve(statusCode.value());
|
||||
if (this.seriesMapping.containsKey(series)) {
|
||||
extract(this.seriesMapping.get(series), response);
|
||||
@@ -156,8 +156,9 @@ public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler
|
||||
}
|
||||
}
|
||||
|
||||
private void extract(@Nullable Class<? extends RestClientException> exceptionClass,
|
||||
ClientHttpResponse response) throws IOException {
|
||||
private void extract(
|
||||
@Nullable Class<? extends RestClientException> exceptionClass, ClientHttpResponse response)
|
||||
throws IOException {
|
||||
|
||||
if (exceptionClass == null) {
|
||||
return;
|
||||
@@ -165,6 +166,7 @@ public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler
|
||||
|
||||
HttpMessageConverterExtractor<? extends RestClientException> extractor =
|
||||
new HttpMessageConverterExtractor<>(exceptionClass, this.messageConverters);
|
||||
|
||||
RestClientException exception = extractor.extractData(response);
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
|
||||
Reference in New Issue
Block a user