1. Обзор
В этом коротком руководстве мы обсудим, как реализовать и внедрить интерфейс ResponseErrorHandler в экземпляр RestTemplate – для изящной обработки ошибок HTTP, возвращаемых удаленными API.
2. Обработка Ошибок по умолчанию
По умолчанию RestTemplate выдаст одно из этих исключений в случае ошибки HTTP:
- HttpClientErrorException – в случае HTTP – статуса 4xx
- Исключение HttpServerErrorException – в случае состояния HTTP 5xx
- UnknownHttpStatusCodeException – в случае неизвестного состояния HTTP
Все эти исключения являются расширениями RestClient Response Exception .
Очевидно, что самая простая стратегия добавления пользовательской обработки ошибок-это обернуть вызов в блок try/catch . Затем мы обрабатываем пойманное исключение так, как считаем нужным.
Однако эта простая стратегия плохо масштабируется по мере увеличения числа удаленных API или вызовов. Было бы более эффективно, если бы мы могли реализовать многоразовый обработчик ошибок для всех наших удаленных вызовов.
3. Реализация ResponseErrorHandler
Итак, класс, реализующий ResponseErrorHandler , будет считывать состояние HTTP из ответа и либо:
- Создайте исключение, которое имеет значение для нашего приложения
- Просто игнорируйте состояние HTTP и позвольте потоку ответов продолжаться без перерыва
Нам нужно внедрить реализацию ResponseErrorHandler в экземпляр RestTemplate .
Следовательно, мы используем Rest TemplateBuilder для создания шаблона и замены DefaultResponseErrorHandler в потоке ответов.
Итак, давайте сначала реализуем наш RestTemplate ResponseErrorHandler:
@Component public class RestTemplateResponseErrorHandler implements ResponseErrorHandler { @Override public boolean hasError(ClientHttpResponse httpResponse) throws IOException { return ( httpResponse.getStatusCode().series() == CLIENT_ERROR || httpResponse.getStatusCode().series() == SERVER_ERROR); } @Override public void handleError(ClientHttpResponse httpResponse) throws IOException { if (httpResponse.getStatusCode() .series() == HttpStatus.Series.SERVER_ERROR) { // handle SERVER_ERROR } else if (httpResponse.getStatusCode() .series() == HttpStatus.Series.CLIENT_ERROR) { // handle CLIENT_ERROR if (httpResponse.getStatusCode() == HttpStatus.NOT_FOUND) { throw new NotFoundException(); } } } }
Затем мы создаем экземпляр RestTemplate с помощью Rest TemplateBuilder, чтобы представить наш RestTemplateResponseErrorHandler :
@Service public class BarConsumerService { private RestTemplate restTemplate; @Autowired public BarConsumerService(RestTemplateBuilder restTemplateBuilder) { RestTemplate restTemplate = restTemplateBuilder .errorHandler(new RestTemplateResponseErrorHandler()) .build(); } public Bar fetchBarById(String barId) { return restTemplate.getForObject("/bars/4242", Bar.class); } }
4. Тестирование Нашей Реализации
Наконец, давайте проверим этот обработчик, издеваясь над сервером и возвращая состояние NOT_FOUND :
@RunWith(SpringRunner.class) @ContextConfiguration(classes = { NotFoundException.class, Bar.class }) @RestClientTest 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("/bars/4242")) .andExpect(method(HttpMethod.GET)) .andRespond(withStatus(HttpStatus.NOT_FOUND)); Bar response = restTemplate .getForObject("/bars/4242", Bar.class); this.server.verify(); } }
5. Заключение
В этой статье представлено решение для реализации и тестирования пользовательского обработчика ошибок для RestTemplate , который преобразует ошибки HTTP в значимые исключения.
Как всегда, код, представленный в этой статье, доступен на Github .