-   Notifications  You must be signed in to change notification settings 
- Fork 38.8k
Description
Affects: spring-web-6.1.11
Code
Let's pretend I don't want to handle status codes in the status handlers.
 RestClient.builder() .defaultStatusHandler(new NoOpResponseErrorHandler()) .build() .get() .uri("http://httpbin.org/status/400") .retrieve() .toEntity(String.class) .getBody()Expected
Default status handler is overridden, there should be no status code handling, no exception thrown.
Actual
org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: [no body]	at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:103) ~[spring-web-6.1.11.jar:6.1.11]	at org.springframework.web.client.StatusHandler.lambda$defaultHandler$3(StatusHandler.java:86) ~[spring-web-6.1.11.jar:6.1.11]	at org.springframework.web.client.StatusHandler.handle(StatusHandler.java:146) ~[spring-web-6.1.11.jar:6.1.11]	at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.applyStatusHandlers(DefaultRestClient.java:698) ~[spring-web-6.1.11.jar:6.1.11]	at org.springframework.web.client.DefaultRestClient.readWithMessageConverters(DefaultRestClient.java:200) ~[spring-web-6.1.11.jar:6.1.11]	at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.readBody(DefaultRestClient.java:685) ~[spring-web-6.1.11.jar:6.1.11]	at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.toEntityInternal(DefaultRestClient.java:655) ~[spring-web-6.1.11.jar:6.1.11]	at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.toEntity(DefaultRestClient.java:644) ~[spring-web-6.1.11.jar:6.1.11] Reason: while the built RestClient contains only a single NoOpResponseErrorHandler, a retrieve() call creates an org.springframework.web.client.DefaultRestClient.DefaultResponseSpec which puts an additional default StatusHandler at the end of the handlers list.
spring-framework/spring-web/src/main/java/org/springframework/web/client/DefaultRestClient.java
Lines 651 to 652 in fc28926
| this.statusHandlers.addAll(DefaultRestClient.this.defaultStatusHandlers); | |
| this.statusHandlers.add(StatusHandler.defaultHandler(DefaultRestClient.this.messageConverters)); | 
and the handlers are tried sequentially
spring-framework/spring-web/src/main/java/org/springframework/web/client/DefaultRestClient.java
Lines 747 to 752 in fc28926
| for (StatusHandler handler : this.statusHandlers) { | |
| if (handler.test(response)) { | |
| handler.handle(this.clientRequest, response); | |
| return; | |
| } | |
| } | 
So, it's only possible to override the default error handler but not possible to replace it.
Workaround
Implement a handler so it returns true from hasError but does nothing in the handler. This looks more like abuse than following a contract.
 RestClient.builder() .defaultStatusHandler(new ResponseErrorHandler() { @Override public boolean hasError(ClientHttpResponse response) throws IOException { return true; } @Override public void handleError(ClientHttpResponse response) { // do nothing } }) .build() .get() .uri("http://httpbin.org/status/400") .retrieve() .toEntity(String.class) .getBody()