Skip to content

Commit 89b2a65

Browse files
committed
DefaultResponseErrorHandler updates
Deprecate handleError(response), and ensure it continues to be invoked if overridden. Closes gh-33980
1 parent a0cc641 commit 89b2a65

9 files changed

+62
-63
lines changed

spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java

+13-23
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
* {@link #hasError(HttpStatusCode)}. Unknown status codes will be ignored by
5050
* {@link #hasError(ClientHttpResponse)}.
5151
*
52-
* <p>See {@link #handleError(ClientHttpResponse)} for more details on specific
53-
* exception types.
52+
* <p>See {@link #handleError(URI, HttpMethod, ClientHttpResponse)} for more
53+
* details on specific exception types.
5454
*
5555
* @author Arjen Poutsma
5656
* @author Rossen Stoyanchev
@@ -116,27 +116,6 @@ protected boolean hasError(int statusCode) {
116116
return (series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR);
117117
}
118118

119-
/**
120-
* Handle the error in the given response with the given resolved status code.
121-
* <p>The default implementation throws:
122-
* <ul>
123-
* <li>{@link HttpClientErrorException} if the status code is in the 4xx
124-
* series, or one of its sub-classes such as
125-
* {@link HttpClientErrorException.BadRequest} and others.
126-
* <li>{@link HttpServerErrorException} if the status code is in the 5xx
127-
* series, or one of its sub-classes such as
128-
* {@link HttpServerErrorException.InternalServerError} and others.
129-
* <li>{@link UnknownHttpStatusCodeException} for error status codes not in the
130-
* {@link HttpStatus} enum range.
131-
* </ul>
132-
* @throws UnknownHttpStatusCodeException in case of an unresolvable status code
133-
* @see #handleError(ClientHttpResponse, HttpStatusCode, URI, HttpMethod)
134-
*/
135-
@Override
136-
public void handleError(ClientHttpResponse response) throws IOException {
137-
handleError(response, response.getStatusCode(), null, null);
138-
}
139-
140119
/**
141120
* Handle the error in the given response with the given resolved status code
142121
* and extra information providing access to the request URL and HTTP method.
@@ -157,9 +136,20 @@ public void handleError(ClientHttpResponse response) throws IOException {
157136
*/
158137
@Override
159138
public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
139+
handleError(response);
160140
handleError(response, response.getStatusCode(), url, method);
161141
}
162142

143+
/**
144+
* {@inheritDoc}
145+
* <p>As of 6.2.1 this method is a no-op unless overridden.
146+
*/
147+
@SuppressWarnings("removal")
148+
@Override
149+
public void handleError(ClientHttpResponse response) throws IOException {
150+
// no-op, but here for backwards compatibility
151+
}
152+
163153
/**
164154
* Handle the error based on the resolved status code.
165155
* <p>The default implementation delegates to

spring-web/src/main/java/org/springframework/web/client/ExtractingResponseErrorHandler.java

+14-11
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,12 @@
4242
* mappings has a match for the {@linkplain ClientHttpResponse#getStatusCode()
4343
* status code} of a given {@code ClientHttpResponse},
4444
* {@link #hasError(ClientHttpResponse)} will return {@code true}, and
45-
* {@link #handleError(ClientHttpResponse)} will attempt to use the
46-
* {@linkplain #setMessageConverters(List) configured message converters} to
47-
* convert the response into the mapped subclass of {@link RestClientException}.
48-
* Note that the {@linkplain #setStatusMapping(Map) status mapping} takes
49-
* precedence over {@linkplain #setSeriesMapping(Map) series mapping}.
45+
* {@link #handleError(ClientHttpResponse, HttpStatusCode, URI, HttpMethod)}
46+
* will attempt to use the {@linkplain #setMessageConverters(List) configured
47+
* message converters} to convert the response into the mapped subclass of
48+
* {@link RestClientException}. Note that the
49+
* {@linkplain #setStatusMapping(Map) status mapping} takes precedence over
50+
* {@linkplain #setSeriesMapping(Map) series mapping}.
5051
*
5152
* <p>If there is no match, this error handler will default to the behavior of
5253
* {@link DefaultResponseErrorHandler}. Note that you can override this default
@@ -98,9 +99,10 @@ public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters
9899
* If this mapping has a match
99100
* for the {@linkplain ClientHttpResponse#getStatusCode() status code} of a given
100101
* {@code ClientHttpResponse}, {@link #hasError(ClientHttpResponse)} will return
101-
* {@code true} and {@link #handleError(ClientHttpResponse)} will attempt to use the
102-
* {@linkplain #setMessageConverters(List) configured message converters} to convert the
103-
* response into the mapped subclass of {@link RestClientException}.
102+
* {@code true} and {@link #handleError(ClientHttpResponse, HttpStatusCode, URI, HttpMethod)}
103+
* will attempt to use the {@linkplain #setMessageConverters(List) configured
104+
* message converters} to convert the response into the mapped subclass of
105+
* {@link RestClientException}.
104106
*/
105107
public void setStatusMapping(Map<HttpStatusCode, Class<? extends RestClientException>> statusMapping) {
106108
if (!CollectionUtils.isEmpty(statusMapping)) {
@@ -113,9 +115,10 @@ public void setStatusMapping(Map<HttpStatusCode, Class<? extends RestClientExcep
113115
* If this mapping has a match
114116
* for the {@linkplain ClientHttpResponse#getStatusCode() status code} of a given
115117
* {@code ClientHttpResponse}, {@link #hasError(ClientHttpResponse)} will return
116-
* {@code true} and {@link #handleError(ClientHttpResponse)} will attempt to use the
117-
* {@linkplain #setMessageConverters(List) configured message converters} to convert the
118-
* response into the mapped subclass of {@link RestClientException}.
118+
* {@code true} and {@link #handleError(ClientHttpResponse, HttpStatusCode, URI, HttpMethod)}
119+
* will attempt to use the {@linkplain #setMessageConverters(List) configured
120+
* message converters} to convert the response into the mapped subclass of
121+
* {@link RestClientException}.
119122
*/
120123
public void setSeriesMapping(Map<HttpStatus.Series, Class<? extends RestClientException>> seriesMapping) {
121124
if (!CollectionUtils.isEmpty(seriesMapping)) {

spring-web/src/main/java/org/springframework/web/client/NoOpResponseErrorHandler.java

-5
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,4 @@ public boolean hasError(ClientHttpResponse response) throws IOException {
4343
return false;
4444
}
4545

46-
@Override
47-
public void handleError(ClientHttpResponse response) throws IOException {
48-
// never actually called
49-
}
50-
5146
}

spring-web/src/main/java/org/springframework/web/client/ResponseErrorHandler.java

+13-8
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,6 @@ public interface ResponseErrorHandler {
4545
* Handle the error in the given response.
4646
* <p>This method is only called when {@link #hasError(ClientHttpResponse)}
4747
* has returned {@code true}.
48-
* @param response the response with the error
49-
* @throws IOException in case of I/O errors
50-
*/
51-
void handleError(ClientHttpResponse response) throws IOException;
52-
53-
/**
54-
* Alternative to {@link #handleError(ClientHttpResponse)} with extra
55-
* information providing access to the request URL and HTTP method.
5648
* @param url the request URL
5749
* @param method the HTTP method
5850
* @param response the response with the error
@@ -63,4 +55,17 @@ default void handleError(URI url, HttpMethod method, ClientHttpResponse response
6355
handleError(response);
6456
}
6557

58+
/**
59+
* Handle the error in the given response.
60+
* <p>This method is only called when {@link #hasError(ClientHttpResponse)}
61+
* has returned {@code true}.
62+
* @param response the response with the error
63+
* @throws IOException in case of I/O errors
64+
* @deprecated in favor of {@link #handleError(URI, HttpMethod, ClientHttpResponse)}
65+
*/
66+
@Deprecated(since = "6.2.1", forRemoval = true)
67+
default void handleError(ClientHttpResponse response) throws IOException {
68+
// no-op unless overridden
69+
}
70+
6671
}

spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerHttpStatusTests.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.web.client;
1818

19+
import java.net.URI;
1920
import java.util.stream.Stream;
2021

2122
import org.junit.jupiter.api.DisplayName;
@@ -24,6 +25,7 @@
2425
import org.junit.jupiter.params.provider.MethodSource;
2526

2627
import org.springframework.http.HttpHeaders;
28+
import org.springframework.http.HttpMethod;
2729
import org.springframework.http.HttpStatus;
2830
import org.springframework.http.MediaType;
2931
import org.springframework.http.client.ClientHttpResponse;
@@ -80,7 +82,8 @@ void handleErrorException(HttpStatus httpStatus, Class<? extends Throwable> expe
8082
given(this.response.getStatusCode()).willReturn(httpStatus);
8183
given(this.response.getHeaders()).willReturn(headers);
8284

83-
assertThatExceptionOfType(expectedExceptionClass).isThrownBy(() -> this.handler.handleError(this.response));
85+
assertThatExceptionOfType(expectedExceptionClass)
86+
.isThrownBy(() -> this.handler.handleError(URI.create("/"), HttpMethod.GET, this.response));
8487
}
8588

8689
static Stream<Arguments> errorCodes() {

spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java

+8-7
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ void handleError() throws Exception {
7575
given(response.getBody()).willReturn(new ByteArrayInputStream("Hello World".getBytes(UTF_8)));
7676

7777
assertThatExceptionOfType(HttpClientErrorException.class)
78-
.isThrownBy(() -> handler.handleError(response))
79-
.withMessage("404 Not Found: \"Hello World\"")
78+
.isThrownBy(() -> handler.handleError(URI.create("/"), HttpMethod.GET, response))
79+
.withMessage("404 Not Found on GET request for \"/\": \"Hello World\"")
8080
.satisfies(ex -> assertThat(ex.getResponseHeaders()).isEqualTo(headers));
8181
}
8282

@@ -127,7 +127,8 @@ void handleErrorIOException() throws Exception {
127127
given(response.getHeaders()).willReturn(headers);
128128
given(response.getBody()).willThrow(new IOException());
129129

130-
assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() -> handler.handleError(response));
130+
assertThatExceptionOfType(HttpClientErrorException.class)
131+
.isThrownBy(() -> handler.handleError(URI.create("/"), HttpMethod.GET, response));
131132
}
132133

133134
@Test
@@ -140,7 +141,7 @@ void handleErrorNullResponse() throws Exception {
140141
given(response.getHeaders()).willReturn(headers);
141142

142143
assertThatExceptionOfType(HttpClientErrorException.class).isThrownBy(() ->
143-
handler.handleError(response));
144+
handler.handleError(URI.create("/"), HttpMethod.GET, response));
144145
}
145146

146147
@Test // SPR-16108
@@ -165,7 +166,7 @@ public void handleErrorUnknownStatusCode() throws Exception {
165166
given(response.getHeaders()).willReturn(headers);
166167

167168
assertThatExceptionOfType(UnknownHttpStatusCodeException.class).isThrownBy(() ->
168-
handler.handleError(response));
169+
handler.handleError(URI.create("/"), HttpMethod.GET, response));
169170
}
170171

171172
@Test // SPR-17461
@@ -196,7 +197,7 @@ void handleErrorForCustomClientError() throws Exception {
196197
given(response.getHeaders()).willReturn(headers);
197198
given(response.getBody()).willReturn(body);
198199

199-
Throwable throwable = catchThrowable(() -> handler.handleError(response));
200+
Throwable throwable = catchThrowable(() -> handler.handleError(URI.create("/"), HttpMethod.GET, response));
200201

201202
// validate exception
202203
assertThat(throwable).isInstanceOf(HttpClientErrorException.class);
@@ -236,7 +237,7 @@ void handleErrorForCustomServerError() throws Exception {
236237
given(response.getHeaders()).willReturn(headers);
237238
given(response.getBody()).willReturn(body);
238239

239-
Throwable throwable = catchThrowable(() -> handler.handleError(response));
240+
Throwable throwable = catchThrowable(() -> handler.handleError(URI.create("/"), HttpMethod.GET, response));
240241

241242
// validate exception
242243
assertThat(throwable).isInstanceOf(HttpServerErrorException.class);

spring-web/src/test/java/org/springframework/web/client/ExtractingResponseErrorHandlerTests.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.web.client;
1818

1919
import java.io.ByteArrayInputStream;
20+
import java.net.URI;
2021
import java.nio.charset.StandardCharsets;
2122
import java.util.Collections;
2223
import java.util.List;
@@ -26,6 +27,7 @@
2627
import org.junit.jupiter.api.Test;
2728

2829
import org.springframework.http.HttpHeaders;
30+
import org.springframework.http.HttpMethod;
2931
import org.springframework.http.HttpStatus;
3032
import org.springframework.http.MediaType;
3133
import org.springframework.http.client.ClientHttpResponse;
@@ -98,7 +100,7 @@ void handleErrorStatusMatch() throws Exception {
98100
given(this.response.getBody()).willReturn(new ByteArrayInputStream(body));
99101

100102
assertThatExceptionOfType(MyRestClientException.class)
101-
.isThrownBy(() -> this.errorHandler.handleError(this.response))
103+
.isThrownBy(() -> this.errorHandler.handleError(URI.create("/"), HttpMethod.GET, this.response))
102104
.satisfies(ex -> assertThat(ex.getFoo()).isEqualTo("bar"));
103105
}
104106

@@ -114,7 +116,7 @@ void handleErrorSeriesMatch() throws Exception {
114116
given(this.response.getBody()).willReturn(new ByteArrayInputStream(body));
115117

116118
assertThatExceptionOfType(MyRestClientException.class)
117-
.isThrownBy(() -> this.errorHandler.handleError(this.response))
119+
.isThrownBy(() -> this.errorHandler.handleError(URI.create("/"), HttpMethod.GET, this.response))
118120
.satisfies(ex -> assertThat(ex.getFoo()).isEqualTo("bar"));
119121
}
120122

@@ -130,7 +132,7 @@ void handleNoMatch() throws Exception {
130132
given(this.response.getBody()).willReturn(new ByteArrayInputStream(body));
131133

132134
assertThatExceptionOfType(HttpClientErrorException.class)
133-
.isThrownBy(() -> this.errorHandler.handleError(this.response))
135+
.isThrownBy(() -> this.errorHandler.handleError(URI.create("/"), HttpMethod.GET, this.response))
134136
.satisfies(ex -> {
135137
assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
136138
assertThat(ex.getResponseBodyAsByteArray()).isEqualTo(body);
@@ -150,7 +152,7 @@ void handleNoMatchOverride() throws Exception {
150152
responseHeaders.setContentLength(body.length);
151153
given(this.response.getBody()).willReturn(new ByteArrayInputStream(body));
152154

153-
this.errorHandler.handleError(this.response);
155+
this.errorHandler.handleError(URI.create("/"), HttpMethod.GET, this.response);
154156
}
155157

156158

spring-web/src/test/java/org/springframework/web/client/RestClientObservationTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,12 @@ static class ObservationErrorHandler implements ResponseErrorHandler {
345345
}
346346

347347
@Override
348-
public boolean hasError(ClientHttpResponse response) throws IOException {
348+
public boolean hasError(ClientHttpResponse response) {
349349
return true;
350350
}
351351

352352
@Override
353-
public void handleError(ClientHttpResponse response) throws IOException {
353+
public void handleError(URI uri, HttpMethod httpMethod, ClientHttpResponse response) {
354354
assertThat(this.observationRegistry.getCurrentObservationScope()).isNotNull();
355355
}
356356
}

spring-web/src/test/java/org/springframework/web/client/RestTemplateObservationTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,12 @@ static class ObservationErrorHandler implements ResponseErrorHandler {
242242
}
243243

244244
@Override
245-
public boolean hasError(ClientHttpResponse response) throws IOException {
245+
public boolean hasError(ClientHttpResponse response) {
246246
return true;
247247
}
248248

249249
@Override
250-
public void handleError(ClientHttpResponse response) throws IOException {
250+
public void handleError(URI uri, HttpMethod httpMethod, ClientHttpResponse response) {
251251
currentObservation = this.observationRegistry.getCurrentObservation();
252252
}
253253
}

0 commit comments

Comments
 (0)