background
This is due to the RestTemplate default response status code handling mechanism. By default, RestTemplate will throw the following exception if an HTTP error occurs:
- HttpClientErrorException– In case of HTTP status 4xx
- HttpServerErrorException – in case of HTTP status 5xx
- UnknownHttpStatusCodeException – in case of an unknown HTTP status
The exceptions are expanding since RestClientResponseException, but obviously can not meet our actual business needs, today, We’ll discuss how to implement and inject the ResponseErrorHandler interface in the RestTemplate instance to gracefully handle HTTP errors returned by the remote API.
Implement ResponseErrorHandler
-
Get the HTTP return status via ResponseErrorHandler and customize the processing logic based on our actual business
-
Implement a custom RestTemplateResponseErrorHandler
@Component public class RestTemplateResponseErrorHandler implements ResponseErrorHandler { @Override public boolean hasError(ClientHttpResponse httpResponse)throws IOException { return (httpResponse.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR || httpResponse.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR); } @Override public void handleError(ClientHttpResponse httpResponse)throws IOException { if (httpResponse.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR) { throw new HttpClientErrorException(httpResponse.getStatusCode()); } else if (httpResponse.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR) { if (httpResponse.getStatusCode() == HttpStatus.NOT_FOUND) { throw newNotFoundException(); }}}}Copy the code
-
Inject the ResponseErrorHandler implementation into the RestTemplate instance
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(ClientHttpRequestFactory factory,RestTemplateResponseErrorHandler restTemplateResponseErrorHandler) throws Exception { RestTemplate restTemplate = new RestTemplate(factory); restTemplate.setErrorHandler(restTemplateResponseErrorHandler); return restTemplate; } @Bean public ClientHttpRequestFactory simpleClientHttpRequestFactory(a) { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); // Set the read timeout factory.setReadTimeout(5000); // Set connection timeout time factory.setConnectTimeout(15000); returnfactory; }}Copy the code
test
-
Join the rely on
<! --spring boot test--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> Copy the code
-
Writing test classes
@RunWith(SpringRunner.class) @ContextConfiguration(classes = {NotFoundException.class, UserEntity.class}) @RestClientTest @Slf4j public class RestTemplateResponseErrorHandlerIntegrationTest { @Autowired private MockRestServiceServer server; @Autowired private RestTemplateBuilder builder; @Test(expected = NotFoundException.class) public void givenRemoteApiCall_when404Error_thenThrowNotFound(a) { Assert.assertNotNull(this.builder); Assert.assertNotNull(this.server); RestTemplate restTemplate = this.builder .errorHandler(new RestTemplateResponseErrorHandler()) .build(); this.server .expect(ExpectedCount.once(), requestTo("/user/load/1000")) .andExpect(method(HttpMethod.GET)) .andRespond(withStatus(HttpStatus.NOT_FOUND)); restTemplate.getForObject("/user/load/1000", UserEntity.class); this.server.verify(); }}Copy the code
-
The results of
The 2020-03-30 14:15:27. 503 INFO/aop - spel,,, 8784 - [the main] plateResponseErrorHandlerIntegrationTest: No active profileset, falling back to default profiles: The default 2020-03-30 14:15:28. 233 INFO/aop - spel,,, 8784 - [the main] plateResponseErrorHandlerIntegrationTest: Started RestTemplateResponseErrorHandlerIntegrationTestin4.541 seconds (JVM is runningfor 8.036) Copy the code