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:

  1. HttpClientErrorException– In case of HTTP status 4xx
  2. HttpServerErrorException – in case of HTTP status 5xx
  3. 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

  1. Get the HTTP return status via ResponseErrorHandler and customize the processing logic based on our actual business

  2. 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
  3. 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

  1. 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
  2. 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
  3. 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