Spring RestTemplate错误处理

1,174 阅读2分钟

背景

在实际开发中使用RestTemplate 经常会出现返回非200状态码报异常,这是源于RestTemplate 默认响应状态码处理机制,默认情况下RestTemplate如果发生HTTP错误将会抛出以下异常:

  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

这个异常都是拓展自RestClientResponseException,但是很明显不能够满足我们的实际业务需求,今天,我们将讨论如何在RestTemplate实例中实现和注入ResponseErrorHandler接口,优雅地处理远程API返回的HTTP错误。

实现ResponseErrorHandler

  1. 通过ResponseErrorHandler获取HTTP返回状态,根据我们的实际业务来自定义处理逻辑

  2. 实现自定义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 new NotFoundException();
                }
            }
        }
    }
    
  3. ResponseErrorHandler实现注入到RestTemplate实例中

    @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() {
            SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
            //读取超时时间设置
            factory.setReadTimeout(5000);
            //连接超时时间设置
            factory.setConnectTimeout(15000);
            return factory;
        }
    }
    

测试

  1. 加入依赖

    <!--spring boot test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
  2. 编写测试类

    @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() {
            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();
        }
    }
    
  3. 结果

    2020-03-30 14:15:27.503  INFO [aop-spel,,,] 8784 --- [           main] plateResponseErrorHandlerIntegrationTest : No active profile set, falling back to default profiles: default
    2020-03-30 14:15:28.233  INFO [aop-spel,,,] 8784 --- [           main] plateResponseErrorHandlerIntegrationTest : Started RestTemplateResponseErrorHandlerIntegrationTest in 4.541 seconds (JVM running for 8.036)