查看原文
其他

关注点分离之RestTemplate的错误处理

程序猿DD 2019-07-13

作者:JackieTang  

来源:生活点亮技术


1. 概览


在这个简短的教程中,将讨论如何实现一个自定义ResponseErrorHandler类并将其注入到RestTemplate实例中去,这样我们就可以在调用远程API时优雅地处理HTTP错误。


2. 默认的错误处理器


默认情况下,如果出现HTTP错误,RestTemplate将抛出下面所列的某一个异常:

  1. HttpClientErrorException –如果HTTP状态码为4 xx

  2. HttpServerErrorException –如果HTTP状态码为5xx

  3. UnknownHttpStatusCodeException –如果是一个未知的HTTP状态码


所有上面的异常类都继承了RestClientResponseException。


显然,添加自定义错误处理的最简单策略,是将调用逻辑嵌在try/catch块中。然后,我们根据需要,来处理捕获的异常。


但是,如果远程API的个数增加或单个API被多个地方调用,相应的try/catch块也会随之增加,即这个简单的策略并不具有很好的扩展性。如果我们所有的远程调用都复用一个错误处理器,那就会更高效。


3. 实现一个自定义ResponseErrorHandler


根据上面的需求,我们下面要实现的自定义ResponseErrorHandler,应该能够从响应中读取HTTP状态,并且:

  1. 抛出一个对我们的应用程序有意义的异常

  2. 简单处理,即直接忽略HTTP状态码,并让响应流连续不中断


并且,实现ResponseErrorHandler接口的自定义处理器需要注入到RestTemplate实例中。 具体而言,我们需要使用RestTemplateBuilder来构建RestTemplate,并在响应流中替换DefaultResponseErrorHandler。


那么,让我们先来创建一个自定义处理器RestTemplateResponseErrorHandler吧:

  1. @Component

  2. public class RestTemplateResponseErrorHandler

  3.  implements ResponseErrorHandler {

  4.    @Override

  5.    public boolean hasError(ClientHttpResponse httpResponse)

  6.      throws IOException {

  7.        return (

  8.          httpResponse.getStatusCode().series() == CLIENT_ERROR

  9.          || httpResponse.getStatusCode().series() == SERVER_ERROR);

  10.    }

  11.    @Override

  12.    public void handleError(ClientHttpResponse httpResponse)

  13.      throws IOException {

  14.        if (httpResponse.getStatusCode()

  15.          .series() == HttpStatus.Series.SERVER_ERROR) {

  16.            // handle SERVER_ERROR

  17.        } else if (httpResponse.getStatusCode()

  18.          .series() == HttpStatus.Series.CLIENT_ERROR) {

  19.            // handle CLIENT_ERROR

  20.            if (httpResponse.getStatusCode() == HttpStatus.NOT_FOUND) {

  21.                throw new NotFoundException();

  22.            }

  23.        }

  24.    }

  25. }


接下来,我们使用RestTemplateBuilder来初始化RestTemplate实例,这样就可以加载前面创建的RestTemplateResponseErrorHandler类:

  1. @Service

  2. public class BarConsumerService {

  3.    private RestTemplate restTemplate;

  4.    @Autowired

  5.    public BarConsumerService(RestTemplateBuilder restTemplateBuilder) {

  6.        RestTemplate restTemplate = restTemplateBuilder

  7.          .errorHandler(new RestTemplateResponseErrorHandler())

  8.          .build();

  9.    }

  10.    public Bar fetchBarById(String barId) {

  11.        return restTemplate.getForObject("/bars/4242", Bar.class);

  12.    }

  13. }


4. 测试处理器


最后,让我们通过mock一个服务器,并让服务器返回一个NOT_FOUND HTTP状态码来测试这个自定义处理器:

  1. @RunWith(SpringRunner.class)

  2. @ContextConfiguration(classes = { NotFoundException.class, Bar.class })

  3. @RestClientTest

  4. public class RestTemplateResponseErrorHandlerIntegrationTest {

  5.    @Autowired

  6.    private MockRestServiceServer server;

  7.    @Autowired

  8.    private RestTemplateBuilder builder;

  9.    @Test(expected = NotFoundException.class)

  10.    public void  givenRemoteApiCall_when404Error_thenThrowNotFound() {

  11.        Assert.assertNotNull(this.builder);

  12.        Assert.assertNotNull(this.server);

  13.        RestTemplate restTemplate = this.builder

  14.          .errorHandler(new RestTemplateResponseErrorHandler())

  15.          .build();

  16.        this.server

  17.          .expect(ExpectedCount.once(), requestTo("/bars/4242"))

  18.          .andExpect(method(HttpMethod.GET))

  19.          .andRespond(withStatus(HttpStatus.NOT_FOUND));

  20.        Bar response = restTemplate

  21.          .getForObject("/bars/4242", Bar.class);

  22.        this.server.verify();

  23.    }

  24. }


5. 总结


本文提供了一个解决方案,用于实现和测试RestTemplate的自定义错误处理器,该处理器可以将HTTP错误转换为有意义的异常。

与往常一样,本文中提供的代码可以在Github上找到。


原文链接:https://www.baeldung.com/spring-rest-template-error-handling

作者: baeldung

译者:helloworldtang


-END-

 近期热文:

关注我

点击“阅读原文”,看本号其他精彩内容

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存