Skip to content

Document that NoOpResponseErrorHandler is to be used with the RestTemplate #33276

@pelepelin

Description

@pelepelin

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.

this.statusHandlers.addAll(DefaultRestClient.this.defaultStatusHandlers);
this.statusHandlers.add(StatusHandler.defaultHandler(DefaultRestClient.this.messageConverters));

and the handlers are tried sequentially

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()

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: documentationA documentation task

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions