Tratamento de erros RestTemplate de primavera
1. Visão geral
Neste breve tutorial, discutiremos como implementar e injetar a interfaceResponseErrorHandler em uma instânciaRestTemplate - para lidar com erros de HTTP retornados por APIs remotas.
2. Tratamento de erros padrão
Por padrão, oRestTemplate lançará uma dessas exceções no caso de um erro HTTP:
-
HttpClientErrorException - em caso de status HTTP 4xx
-
HttpServerErrorException – in caso de status HTTP 5xx
-
UnknownHttpStatusCodeException – caso sin de um status HTTP desconhecido
Todas essas exceções são extensões deRestClientResponseException.
Obviously, the simplest strategy to add a custom error handling is to wrap the call in a try/catch block. Em seguida, processamos a exceção capturada como achamos adequado.
No entanto,this simple strategy doesn’t scale well conforme o número de APIs ou chamadas remotas aumenta. Seria mais eficiente se pudéssemos implementar um manipulador de erros reutilizável para todas as nossas chamadas remotas.
3. Implementando umResponseErrorHandler
E assim, uma classe que implementaResponseErrorHandler will lê o status HTTP da resposta e:
-
Lance uma exceção significativa para nosso aplicativo
-
Simplesmente ignore o status HTTP e deixe o fluxo de resposta continuar sem interrupção
Precisamos injetar a implementaçãoResponseErrorHandler na instânciaRestTemplate.
Portanto, usamosRestTemplateBuilder para construir o modelo e substituirDefaultResponseErrorHandler no fluxo de resposta.
Então, vamos primeiro implementar nossoRestTemplateResponseErrorHandler:
@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();
}
}
}
}
Em seguida, construímos a instânciaRestTemplate usando o sintroduceRestTemplateBuilder to nossoRestTemplateResponseErrorHandler:
@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. Testando nossa implementação
Finalmente, vamos testar este manipulador simulando um servidor e retornando um statusNOT_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. Conclusão
Este artigo apresentou uma solução para implementar e testar um manipulador de erros personalizado para umRestTemplate que converte erros HTTP em exceções significativas.
Como sempre, o código apresentado neste artigo está disponívelover on Github.