web-dev-qa-db-fra.com

Gestion des exceptions Spring Resttemplate

Ci-dessous l'extrait de code; En gros, j'essaie de propager l'exception lorsque le code d'erreur est autre que 200.

ResponseEntity<Object> response = restTemplate.exchange(url.toString().replace("{version}", version),
                    HttpMethod.POST, entity, Object.class);
            if(response.getStatusCode().value()!= 200){
                logger.debug("Encountered Error while Calling API");
                throw new ApplicationException();
            }

Cependant, dans le cas d’une réponse 500 du serveur, je reçois l’exception 

org.springframework.web.client.HttpServerErrorException: 500 Internal Server Error
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.Java:94) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]

Dois-je vraiment intégrer la méthode d'échange de modèles de repos à l'essai? Quel serait alors le but des codes?

71
vaibhav

Vous voulez créer une classe qui implémente ResponseErrorHandler et en utiliser une instance pour définir le traitement des erreurs de votre modèle de repos:

public class MyErrorHandler implements ResponseErrorHandler {
  @Override
  public void handleError(ClientHttpResponse response) throws IOException {
    // your error handling here
  }

  @Override
  public boolean hasError(ClientHttpResponse response) throws IOException {
     ...
  }
}

[...]

public static void main(String args[]) {
  RestTemplate restTemplate = new RestTemplate();
  restTemplate.setErrorHandler(new MyErrorHandler());
}

De plus, Spring a la classe DefaultResponseErrorHandler, que vous pouvez étendre au lieu d'implémenter l'interface, au cas où vous voudriez seulement écraser la méthode handleError.

public class MyErrorHandler extends DefaultResponseErrorHandler {
  @Override
  public void handleError(ClientHttpResponse response) throws IOException {
    // your error handling here
  }
}

Jetez un coup d’œil à son code source pour avoir une idée de la façon dont Spring traite les erreurs HTTP.

87
carcaret

Vous devriez attraper une exception HttpStatusCodeException:

try {
    restTemplate.exchange(...);
} catch (HttpStatusCodeException exception) {
    int statusCode = exception.getStatusCode().value();
    ...
}
24
mekazu

Spring traite intelligemment les codes d'erreur http comme des exceptions et suppose que votre code de traitement des exceptions possède le contexte pour gérer l'erreur. Pour que l'échange fonctionne correctement, procédez comme suit:

    try {
        return restTemplate.exchange(url, httpMethod, httpEntity, String.class);
    } catch(HttpStatusCodeException e) {
        return ResponseEntity.status(e.getRawStatusCode()).headers(e.getResponseHeaders())
                .body(e.getResponseBodyAsString());
    }

Cela renverra tous les résultats attendus de la réponse.

11
austin cherlo

Une autre solution est celle décrite ici à la fin de ce message par "enlian": http://springinpractice.com/2013/10/07/handling-json-error-object-responses-with-springs -resttemplate

try{
     restTemplate.exchange(...)
} catch(HttpStatusCodeException e){
     String errorpayload = e.getResponseBodyAsString();
     //do whatever you want
} catch(RestClientException e){
     //no response payload, tell the user sth else 
}
4
Investigator

Aucun de ces tutoriels ou d'autres tutoriels ailleurs ne fonctionnait pour moi. RestTemplate est simplement de la merde. J'écris ceci pour que les autres ne perdent pas de temps à cette horrible chose ... Je vais appeler restTemplate.exchange et obtenir 400 codes d'état. Une exception est également levée sur cette ligne, mais ce n'est pas une exception HttpStatusCodeExode, c'est une exception ResourceAccessException. J'ai essayé d'échanger pour Object et String. J'ai essayé d'inclure ResponseErrorHandler qui est totalement inutile.

Ce n'est pas la première fois que je ne suis pas capable de faire ce que je veux avec RestTemplate non plus et je ne l'utilise même pas depuis longtemps. Ne perds pas de temps.

Vous pouvez par exemple utiliser:

Apache HttpClient: https://mvnrepository.com/artifact/org.Apache.httpcomponents/httpclient

Or OkHttp3: https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp

2
Siamaster

Si vous utilisez le mécanisme de pooling (usine du client http) ou d'équilibrage de la charge (eureka) avec votre RestTemplate, vous n'aurez pas le luxe de créer un new RestTemplate par classe. Si vous appelez plusieurs services, vous ne pouvez pas utiliser setErrorHandler car if serait utilisé globalement pour toutes vos demandes. 

Dans ce cas, attraper le HttpStatusCodeException semble être la meilleure option.

La seule autre option disponible consiste à définir plusieurs instances RestTemplate à l'aide de l'annotation @Qualifier.

Aussi - mais ceci est mon propre goût - j'aime ma gestion des erreurs bien calée sur mes appels. 

1
Hannes

Le code d'échange est ci-dessous :

public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
            HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException

Exception RestClientException a HttpClientErrorException et HttpStatusCodeException exception.

Donc, dans RestTemplete, il peut arriver que HttpClientErrorException et HttpStatusCodeException exception . Dans un objet exception, vous pouvez obtenir un message d'erreur exact de cette manière: exception.getResponseBodyAsString()

Voici l'exemple de code :

public Object callToRestService(HttpMethod httpMethod, String url, Object requestObject, Class<?> responseObject) {

        printLog( "Url : " + url);
        printLog( "callToRestService Request : " + new GsonBuilder().setPrettyPrinting().create().toJson(requestObject));

        try {

            RestTemplate restTemplate = new RestTemplate();
            restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            restTemplate.getMessageConverters().add(new StringHttpMessageConverter());


            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);

            long start = System.currentTimeMillis();

            ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);

            printLog( "callToRestService Status : " + responseEntity.getStatusCodeValue());


            printLog( "callToRestService Body : " + new GsonBuilder().setPrettyPrinting().create().toJson(responseEntity.getBody()));

            long elapsedTime = System.currentTimeMillis() - start;
            printLog( "callToRestService Execution time: " + elapsedTime + " Milliseconds)");

            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                return responseEntity.getBody();
            }

        } catch (HttpClientErrorException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }catch (HttpStatusCodeException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }
        return null;
    }

Voici la description du code :

Dans cette méthode, vous devez transmettre la requête et la classe de réponse. Cette méthode analysera automatiquement la réponse en tant qu'objet demandé. 

Tout d'abord, vous devez ajouter un convertisseur de message.

restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            restTemplate.getMessageConverters().add(new StringHttpMessageConverter());

Ensuite, vous devez ajouter requestHeader. Voici le code:

HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);

Enfin, vous devez appeler la méthode d'échange:

ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);

Pour l’impression prety, j’ai utilisé la bibliothèque Gson . voici le grade: compile 'com.google.code.gson:gson:2.4'

Vous pouvez simplement appeler le code ci-dessous pour obtenir une réponse:

ResponseObject response=new RestExample().callToRestService(HttpMethod.POST,"URL_HERE",new RequestObject(),ResponseObject.class);

Voici le code de travail complet :

import com.google.gson.GsonBuilder;
import org.springframework.http.*;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;


public class RestExample {

    public RestExample() {

    }

    public Object callToRestService(HttpMethod httpMethod, String url, Object requestObject, Class<?> responseObject) {

        printLog( "Url : " + url);
        printLog( "callToRestService Request : " + new GsonBuilder().setPrettyPrinting().create().toJson(requestObject));

        try {

            RestTemplate restTemplate = new RestTemplate();
            restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            restTemplate.getMessageConverters().add(new StringHttpMessageConverter());


            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);

            long start = System.currentTimeMillis();

            ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);

            printLog( "callToRestService Status : " + responseEntity.getStatusCodeValue());


            printLog( "callToRestService Body : " + new GsonBuilder().setPrettyPrinting().create().toJson(responseEntity.getBody()));

            long elapsedTime = System.currentTimeMillis() - start;
            printLog( "callToRestService Execution time: " + elapsedTime + " Milliseconds)");

            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                return responseEntity.getBody();
            }

        } catch (HttpClientErrorException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }catch (HttpStatusCodeException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }
        return null;
    }

    private void printLog(String message){
        System.out.println(message);
    }
}

Merci :)

0
Md. Sajedul Karim

Spring vous fait abstraction de la très très longue liste de codes de statut http. C'est l'idée des exceptions. Examinez la hiérarchie des exceptions org.springframework.web.client.RestClientException:

   

Vous avez un tas de classes pour cartographier les situations les plus courantes lorsqu’il s’agit de réponses http. La liste de codes http est vraiment longue, vous ne voudrez pas écrire du code pour gérer chaque situation. Mais par exemple, jetez un coup d’œil dans la sous-hiérarchie HttpClientErrorException. Vous avez une seule exception pour mapper tout type d'erreur 4xx. Si vous devez aller en profondeur, alors vous pouvez. Mais en attrapant simplement HttpClientErrorException, vous pouvez gérer toute situation dans laquelle des données incorrectes ont été fournies au service.

Le DefaultResponseErrorHandler est vraiment simple et solide. Si le code d'état de la réponse ne fait pas partie de la famille 2xx, il renvoie simplement true pour la méthode hasError.

0
Perimosh