Spring RestTemplate Error Handling

Traitement des erreurs Spring RestTemplate

1. Vue d'ensemble

Dans ce court didacticiel, nous verrons comment implémenter et injecter l'interfaceResponseErrorHandler dans une instanceRestTemplate - pour gérer correctement les erreurs HTTP renvoyées par les API distantes. 

2. Traitement des erreurs par défaut

Par défaut, leRestTemplate lancera l'une de ces exceptions en cas d'erreur HTTP:

  1. HttpClientErrorException - en cas d'état HTTP 4xx

  2. HttpServerErrorException – in cas de l'état HTTP 5xx

  3. UnknownHttpStatusCodeException – in cas d'un état HTTP inconnu

Toutes ces exceptions sont des extensions deRestClientResponseException.

Obviously, the simplest strategy to add a custom error handling is to wrap the call in a try/catch block. Ensuite, nous traitons l'exception interceptée comme bon nous semble.

Cependant,this simple strategy doesn’t scale well à mesure que le nombre d'API ou d'appels distants augmente. Il serait plus efficace d’implémenter un gestionnaire d’erreurs réutilisable pour tous nos appels distants.

3. Implémentation d'unResponseErrorHandler

Et donc, une classe qui implémenteResponseErrorHandler will lit l'état HTTP à partir de la réponse et soit:

  1. Lance une exception significative pour notre application

  2. Ignorer simplement le statut HTTP et laisser le flux de réponse se poursuivre sans interruption

Nous devons injecter l'implémentationResponseErrorHandler dans l'instanceRestTemplate.

Par conséquent, nous utilisons lesRestTemplateBuilder pour construire le modèle et remplacer lesDefaultResponseErrorHandler dans le flux de réponse.

Commençons par implémenter nosRestTemplateResponseErrorHandler:

@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();
            }
        }
    }
}

Ensuite, nous construisons l'instanceRestTemplate en utilisant leRestTemplateBuilder to introduce notreRestTemplateResponseErrorHandler:

@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. Tester notre implémentation

Enfin, testons ce gestionnaire en se moquant d'un serveur et en renvoyant un étatNOT_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. Conclusion

Cet article a présenté une solution pour implémenter et tester un gestionnaire d'erreurs personnalisé pour unRestTemplate qui convertit les erreurs HTTP en exceptions significatives.

Comme toujours, le code présenté dans cet article est disponibleover on Github.