前言
在实际开发中使用RestTemplate 经常会出现返回非200状态码报异常,这是源于RestTemplate 默认响应状态码处理机制,默认情况下RestTemplate如果发生HTTP错误将会抛出以下异常:
- HttpClientErrorException – in case of HTTP status 4xx
- HttpServerErrorException – in case of HTTP status 5xx
- UnknownHttpStatusCodeException – in case of an unknown HTTP status
这个异常都是拓展自RestClientResponseException,但是很明显不能够满足我们的实际业务需求,今天,我们将讨论如何在RestTemplate实例中实现和注入ResponseErrorHandler接口,优雅地处理远程API返回的HTTP错误。
实现ResponseErrorHandler
-
通过ResponseErrorHandler获取HTTP返回状态,根据我们的实际业务来自定义处理逻辑
-
实现自定义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(); } } } }
-
将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; } }
测试
-
加入依赖
<!--spring boot test--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
-
编写测试类
@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(); } }
-
结果
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)