DefaultResponseErrorHandler detects non-standard error code as well
Issue: SPR-17439
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -435,50 +435,62 @@ public enum HttpStatus {
|
||||
return this.reasonPhrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the HTTP status series of this status code.
|
||||
* @see HttpStatus.Series
|
||||
*/
|
||||
public Series series() {
|
||||
return Series.valueOf(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this status code is in the HTTP series
|
||||
* {@link org.springframework.http.HttpStatus.Series#INFORMATIONAL}.
|
||||
* This is a shortcut for checking the value of {@link #series()}.
|
||||
* @see #series()
|
||||
*/
|
||||
public boolean is1xxInformational() {
|
||||
return Series.INFORMATIONAL.equals(series());
|
||||
return (series() == Series.INFORMATIONAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this status code is in the HTTP series
|
||||
* {@link org.springframework.http.HttpStatus.Series#SUCCESSFUL}.
|
||||
* This is a shortcut for checking the value of {@link #series()}.
|
||||
* @see #series()
|
||||
*/
|
||||
public boolean is2xxSuccessful() {
|
||||
return Series.SUCCESSFUL.equals(series());
|
||||
return (series() == Series.SUCCESSFUL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this status code is in the HTTP series
|
||||
* {@link org.springframework.http.HttpStatus.Series#REDIRECTION}.
|
||||
* This is a shortcut for checking the value of {@link #series()}.
|
||||
* @see #series()
|
||||
*/
|
||||
public boolean is3xxRedirection() {
|
||||
return Series.REDIRECTION.equals(series());
|
||||
return (series() == Series.REDIRECTION);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether this status code is in the HTTP series
|
||||
* {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR}.
|
||||
* This is a shortcut for checking the value of {@link #series()}.
|
||||
* @see #series()
|
||||
*/
|
||||
public boolean is4xxClientError() {
|
||||
return Series.CLIENT_ERROR.equals(series());
|
||||
return (series() == Series.CLIENT_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this status code is in the HTTP series
|
||||
* {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR}.
|
||||
* This is a shortcut for checking the value of {@link #series()}.
|
||||
* @see #series()
|
||||
*/
|
||||
public boolean is5xxServerError() {
|
||||
return Series.SERVER_ERROR.equals(series());
|
||||
return (series() == Series.SERVER_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -486,17 +498,12 @@ public enum HttpStatus {
|
||||
* {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR} or
|
||||
* {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR}.
|
||||
* This is a shortcut for checking the value of {@link #series()}.
|
||||
* @since 5.0
|
||||
* @see #is4xxClientError()
|
||||
* @see #is5xxServerError()
|
||||
*/
|
||||
public boolean isError() {
|
||||
return is4xxClientError() || is5xxServerError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP status series of this status code.
|
||||
* @see HttpStatus.Series
|
||||
*/
|
||||
public Series series() {
|
||||
return Series.valueOf(this);
|
||||
return (is4xxClientError() || is5xxServerError());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -522,7 +529,6 @@ public enum HttpStatus {
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve the given status code to an {@code HttpStatus}, if possible.
|
||||
* @param statusCode the HTTP status code (potentially non-standard)
|
||||
@@ -565,18 +571,30 @@ public enum HttpStatus {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static Series valueOf(int status) {
|
||||
int seriesCode = status / 100;
|
||||
/**
|
||||
* Return the enum constant of this type with the corresponding series.
|
||||
* @param status a standard HTTP status enum value
|
||||
* @return the enum constant of this type with the corresponding series
|
||||
* @throws IllegalArgumentException if this enum has no corresponding constant
|
||||
*/
|
||||
public static Series valueOf(HttpStatus status) {
|
||||
return valueOf(status.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the enum constant of this type with the corresponding series.
|
||||
* @param statusCode the HTTP status code (potentially non-standard)
|
||||
* @return the enum constant of this type with the corresponding series
|
||||
* @throws IllegalArgumentException if this enum has no corresponding constant
|
||||
*/
|
||||
public static Series valueOf(int statusCode) {
|
||||
int seriesCode = statusCode / 100;
|
||||
for (Series series : values()) {
|
||||
if (series.value == seriesCode) {
|
||||
return series;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No matching constant for [" + status + "]");
|
||||
}
|
||||
|
||||
public static Series valueOf(HttpStatus status) {
|
||||
return valueOf(status.value);
|
||||
throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,12 +44,29 @@ import org.springframework.util.FileCopyUtils;
|
||||
public class DefaultResponseErrorHandler implements ResponseErrorHandler {
|
||||
|
||||
/**
|
||||
* Delegates to {@link #hasError(HttpStatus)} with the response status code.
|
||||
* 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.
|
||||
* @see ClientHttpResponse#getRawStatusCode()
|
||||
* @see #hasError(HttpStatus)
|
||||
* @see #hasError(int)
|
||||
*/
|
||||
@Override
|
||||
public boolean hasError(ClientHttpResponse response) throws IOException {
|
||||
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
|
||||
return (statusCode != null && hasError(statusCode));
|
||||
int rawStatusCode = response.getRawStatusCode();
|
||||
HttpStatus statusCode = HttpStatus.resolve(rawStatusCode);
|
||||
return (statusCode != null ? hasError(statusCode) : hasError(rawStatusCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method called from {@link #hasError(ClientHttpResponse)}.
|
||||
* <p>The default implementation checks {@link HttpStatus#isError()}.
|
||||
* Can be overridden in subclasses.
|
||||
* @param statusCode the HTTP status code as enum value
|
||||
* @return {@code true} if the response indicates an error; {@code false} otherwise
|
||||
* @see HttpStatus#isError()
|
||||
*/
|
||||
protected boolean hasError(HttpStatus statusCode) {
|
||||
return statusCode.isError();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,16 +75,23 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
|
||||
* {@code HttpStatus.Series#CLIENT_ERROR CLIENT_ERROR} or
|
||||
* {@code HttpStatus.Series#SERVER_ERROR SERVER_ERROR}.
|
||||
* Can be overridden in subclasses.
|
||||
* @param statusCode the HTTP status code
|
||||
* @return {@code true} if the response has an error; {@code false} otherwise
|
||||
* @param unknownStatusCode the HTTP status code as raw value
|
||||
* @return {@code true} if the response indicates an error; {@code false} otherwise
|
||||
* @since 4.3.21
|
||||
* @see HttpStatus.Series#CLIENT_ERROR
|
||||
* @see HttpStatus.Series#SERVER_ERROR
|
||||
*/
|
||||
protected boolean hasError(HttpStatus statusCode) {
|
||||
return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR ||
|
||||
statusCode.series() == HttpStatus.Series.SERVER_ERROR);
|
||||
protected boolean hasError(int unknownStatusCode) {
|
||||
int seriesCode = unknownStatusCode / 100;
|
||||
return (seriesCode == HttpStatus.Series.CLIENT_ERROR.value() ||
|
||||
seriesCode == HttpStatus.Series.SERVER_ERROR.value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link #handleError(ClientHttpResponse, HttpStatus)} with the response status code.
|
||||
* Delegates to {@link #handleError(ClientHttpResponse, HttpStatus)} with the
|
||||
* response status code.
|
||||
* @throws UnknownHttpStatusCodeException in case of an unresolvable status code
|
||||
* @see #handleError(ClientHttpResponse, HttpStatus)
|
||||
*/
|
||||
@Override
|
||||
public void handleError(ClientHttpResponse response) throws IOException {
|
||||
@@ -81,10 +105,10 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
|
||||
|
||||
/**
|
||||
* Handle the error in the given response with the given resolved status code.
|
||||
* <p>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.
|
||||
* <p>The default implementation throws an {@link HttpClientErrorException}
|
||||
* if the status code is {@link HttpStatus.Series#CLIENT_ERROR}, an
|
||||
* {@link HttpServerErrorException} if it is {@link HttpStatus.Series#SERVER_ERROR},
|
||||
* and an {@link UnknownHttpStatusCodeException} in other cases.
|
||||
* @since 5.0
|
||||
*/
|
||||
protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -36,7 +36,7 @@ public interface ResponseErrorHandler {
|
||||
* <p>Implementations will typically inspect the
|
||||
* {@link ClientHttpResponse#getStatusCode() HttpStatus} of the response.
|
||||
* @param response the response to inspect
|
||||
* @return {@code true} if the response has an error; {@code false} otherwise
|
||||
* @return {@code true} if the response indicates an error; {@code false} otherwise
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
boolean hasError(ClientHttpResponse response) throws IOException;
|
||||
|
||||
@@ -65,7 +65,7 @@ public class DefaultResponseErrorHandlerTests {
|
||||
given(response.getRawStatusCode()).willReturn(HttpStatus.NOT_FOUND.value());
|
||||
given(response.getStatusText()).willReturn("Not Found");
|
||||
given(response.getHeaders()).willReturn(headers);
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream("Hello World".getBytes("UTF-8")));
|
||||
given(response.getBody()).willReturn(new ByteArrayInputStream("Hello World".getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
try {
|
||||
handler.handleError(response);
|
||||
@@ -101,18 +101,6 @@ public class DefaultResponseErrorHandlerTests {
|
||||
handler.handleError(response);
|
||||
}
|
||||
|
||||
@Test(expected = UnknownHttpStatusCodeException.class) // SPR-9406
|
||||
public void unknownStatusCode() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.TEXT_PLAIN);
|
||||
|
||||
given(response.getRawStatusCode()).willReturn(999);
|
||||
given(response.getStatusText()).willReturn("Custom status code");
|
||||
given(response.getHeaders()).willReturn(headers);
|
||||
|
||||
handler.handleError(response);
|
||||
}
|
||||
|
||||
@Test // SPR-16108
|
||||
public void hasErrorForUnknownStatusCode() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
@@ -125,6 +113,66 @@ public class DefaultResponseErrorHandlerTests {
|
||||
assertFalse(handler.hasError(response));
|
||||
}
|
||||
|
||||
@Test(expected = UnknownHttpStatusCodeException.class) // SPR-9406
|
||||
public void handleErrorUnknownStatusCode() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.TEXT_PLAIN);
|
||||
|
||||
given(response.getRawStatusCode()).willReturn(999);
|
||||
given(response.getStatusText()).willReturn("Custom status code");
|
||||
given(response.getHeaders()).willReturn(headers);
|
||||
|
||||
handler.handleError(response);
|
||||
}
|
||||
|
||||
@Test // SPR-17461
|
||||
public void hasErrorForCustomClientError() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.TEXT_PLAIN);
|
||||
|
||||
given(response.getRawStatusCode()).willReturn(499);
|
||||
given(response.getStatusText()).willReturn("Custom status code");
|
||||
given(response.getHeaders()).willReturn(headers);
|
||||
|
||||
assertTrue(handler.hasError(response));
|
||||
}
|
||||
|
||||
@Test(expected = UnknownHttpStatusCodeException.class)
|
||||
public void handleErrorForCustomClientError() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.TEXT_PLAIN);
|
||||
|
||||
given(response.getRawStatusCode()).willReturn(499);
|
||||
given(response.getStatusText()).willReturn("Custom status code");
|
||||
given(response.getHeaders()).willReturn(headers);
|
||||
|
||||
handler.handleError(response);
|
||||
}
|
||||
|
||||
@Test // SPR-17461
|
||||
public void hasErrorForCustomServerError() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.TEXT_PLAIN);
|
||||
|
||||
given(response.getRawStatusCode()).willReturn(599);
|
||||
given(response.getStatusText()).willReturn("Custom status code");
|
||||
given(response.getHeaders()).willReturn(headers);
|
||||
|
||||
assertTrue(handler.hasError(response));
|
||||
}
|
||||
|
||||
@Test(expected = UnknownHttpStatusCodeException.class)
|
||||
public void handleErrorForCustomServerError() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.TEXT_PLAIN);
|
||||
|
||||
given(response.getRawStatusCode()).willReturn(599);
|
||||
given(response.getStatusText()).willReturn("Custom status code");
|
||||
given(response.getHeaders()).willReturn(headers);
|
||||
|
||||
handler.handleError(response);
|
||||
}
|
||||
|
||||
@Test // SPR-16604
|
||||
public void bodyAvailableAfterHasErrorForUnknownStatusCode() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
Reference in New Issue
Block a user