Spring Resttemplate Ausnahmebehandlung

124

Unten ist das Code-Snippet; Im Grunde versuche ich, die Ausnahme zu verbreiten, wenn der Fehlercode etwas anderes als 200 ist.

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

Bei einer Antwort von 500 vom Server erhalte ich jedoch die Ausnahme

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]

Muss ich die Methode zum Austausch von Restvorlagen wirklich in try umbrechen? Was wäre dann der Zweck von Codes?

Vaibhav
quelle
Bitte teilen Sie den Code von ApplicationException ()
Mudassar

Antworten:

128

Sie möchten eine Klasse erstellen, die implementiert, ResponseErrorHandlerund dann eine Instanz davon verwenden, um die Fehlerbehandlung Ihrer Restvorlage festzulegen:

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

Außerdem verfügt Spring über die Klasse DefaultResponseErrorHandler, die Sie erweitern können, anstatt die Schnittstelle zu implementieren, falls Sie die handleErrorMethode nur überschreiben möchten .

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

Schauen Sie sich den Quellcode an, um eine Vorstellung davon zu bekommen, wie Spring mit HTTP-Fehlern umgeht.

Carcaret
quelle
1
Ich habe 1 Instanz von RestTemplate, die ich für verschiedene Aufrufe wiederverwenden kann. Ich muss Fehler von verschiedenen Aufrufen unterschiedlich behandeln - anscheinend gibt es keine Möglichkeit, dies mit einem globalen Handler zu tun - ich muss einen Handler pro Anfrage bereitstellen.
MVMN
4
Mit diesem Fehlerbehandler bekomme ich immer ein, ResourceAccessExceptionweil s RestTemplate.doExecutefängt IOExceptionund a wirft ResourceAccessException. Was mache ich falsch?
Federico Bellucci
Ich konnte dieses Problem lösen, indem ich DefaultResponseErrorHandler erweiterte.
Crenguta S
48

Sie sollten eine HttpStatusCodeExceptionAusnahme abfangen :

try {
    restTemplate.exchange(...);
} catch (HttpStatusCodeException exception) {
    int statusCode = exception.getStatusCode().value();
    ...
}
Mekazu
quelle
37
IMO sollte die Antwort immer mit einem geeigneten Statuscode kommen, sonst was ist der Zweck von Codes.
Vaibhav
5
Ich bin mir nicht sicher, ob ich den Einwand von @vaibhav verstehe: Das Abfangen von HttpStatusCodeException ist nicht für einen falschen Code, sondern weil in vielen Fällen immer eine Ausnahme ausgelöst wird und Ihr if (code == value) daher niemals ausgeführt werden kann.
Stefano Scarpanti
1
Ausnahmen sind in Java sehr kostspielig. Es ist in Ordnung für gelegentliche, unerwartete Ursachen (daher der Name), aber darüber hinaus sollten Sie stattdessen nach anderen Lösungen suchen.
Agoston Horvath
11
"Sehr teuer"? Teurer als beispielsweise ein HTTP-Anruf?
IcedDante
4
@RaffaelBecharaRameh - HttpStatusCodeException .getResponseBodyAsString () oder HttpStatusCodeException.getResponseBodyAsByteArray ().
Dave
45

Spring behandelt http-Fehlercodes geschickt als Ausnahmen und geht davon aus, dass Ihr Ausnahmebehandlungscode den Kontext hat, um den Fehler zu behandeln. Gehen Sie folgendermaßen vor, damit der Austausch so funktioniert, wie Sie es erwarten würden:

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

Dadurch werden alle erwarteten Ergebnisse der Antwort zurückgegeben.

Austin Cherlo
quelle
2
Sie müssen einen anderen HttpClient als das Standard-SDK verwenden, um den Antworttext für Fehler zu erhalten
Rasiermesser
26

Eine andere Lösung ist die hier am Ende dieses Beitrags von "enlian" beschriebene: 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 
}
Ermittler
quelle
4
Sie müssen einen anderen HttpClient als das Standard-SDK verwenden, um den Antworttext für Fehler zu erhalten (z. B. Apache Commons HttpClient)
Rasiermesser
17

Spring abstrahiert Sie von der sehr sehr sehr großen Liste des http-Statuscodes. Das ist die Idee der Ausnahmen. Schauen Sie sich die Hierarchie von org.springframework.web.client.RestClientException an:

Sie haben eine Reihe von Klassen, um die häufigsten Situationen beim Umgang mit http-Antworten abzubilden. Die Liste der http-Codes ist sehr umfangreich. Sie möchten nicht, dass Code für jede Situation geschrieben wird. Schauen Sie sich beispielsweise die Unterhierarchie HttpClientErrorException an. Sie haben eine einzige Ausnahme, um 4xx-Fehler zuzuordnen. Wenn Sie tief gehen müssen, dann können Sie. Mit nur dem Abfangen von HttpClientErrorException können Sie jedoch jede Situation bewältigen, in der dem Dienst fehlerhafte Daten bereitgestellt wurden.

Der DefaultResponseErrorHandler ist wirklich einfach und solide. Wenn der Antwortstatuscode nicht aus der Familie der 2xx stammt, gibt er nur true für die hasError-Methode zurück.

Perimosh
quelle
Alter, danke für die Erklärung. Wie haben Sie diesen Baum mit Ausnahmehierarchie erstellt?
Standalone
1
Hey Mann, ich habe Eclipse benutzt. Drücken Sie einfach Strg + Umschalt + T, um den Typensucher zu öffnen, und geben Sie RestClientException ein. Doppelklicken Sie in den Ergebnissen auf RestClientException. Eclipse öffnet diese Klasse für Sie. Setzen Sie dann den Mauszeiger über den Klassennamen (dort steht "public class RestClientException ..." und drücken Sie
Strg
Hast Du es versucht?
Perimosh
1
Übrigens in Intellij ist es: Klicken Sie auf die Klasse im Projektbaum und Strg + Alt + U oder klicken Sie mit der rechten Maustaste -> Diagramm erstellen
Standalone
3

Wenn Sie einen Pooling-Mechanismus (http client factory) oder einen Lastausgleichsmechanismus (eureka) mit Ihrem verwenden RestTemplate, haben Sie nicht den Luxus, einen new RestTemplatepro Klasse zu erstellen . Wenn Sie mehr als einen Dienst anrufen, können Sie ihn nicht verwenden, setErrorHandlerda er global für alle Ihre Anforderungen verwendet wird.

In diesem Fall HttpStatusCodeExceptionscheint es die bessere Option zu sein , das zu fangen .

Die einzige andere Möglichkeit besteht darin, RestTemplatemithilfe der @QualifierAnnotation mehrere Instanzen zu definieren .

Auch - aber das ist mein eigener Geschmack - ich mag es, wenn sich meine Fehlerbehandlung eng an meine Anrufe schmiegt.

Hannes
quelle
3

Ich habe das wie folgt gehandhabt:

try {
  response = restTemplate.postForEntity(requestUrl, new HttpEntity<>(requestBody, headers), String.class);
} catch (HttpStatusCodeException ex) {
  response = new ResponseEntity<String>(ex.getResponseBodyAsString(), ex.getResponseHeaders(), ex.getStatusCode());
}
Saikat
quelle
1

Der Umtauschcode ist unten :

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

Ausnahme RestClientExceptionhat HttpClientErrorExceptionund HttpStatusCodeExceptionAusnahme.

So in RestTempletedort occure kann HttpClientErrorExceptionund HttpStatusCodeExceptionAusnahme. Im Ausnahmeobjekt können Sie auf folgende Weise eine genaue Fehlermeldung erhalten:exception.getResponseBodyAsString()

Hier ist der Beispielcode :

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;
    }

Hier ist die Codebeschreibung :

Bei dieser Methode müssen Sie die Anforderungs- und Antwortklasse übergeben. Diese Methode analysiert die Antwort automatisch als angefordertes Objekt.

Zunächst müssen Sie den Nachrichtenkonverter hinzufügen.

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

Dann müssen Sie hinzufügen requestHeader. Hier ist der Code:

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

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

Schließlich müssen Sie die Austauschmethode aufrufen:

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

Für den Prety-Druck habe ich die Gson-Bibliothek verwendet. hier ist der gradle:compile 'com.google.code.gson:gson:2.4'

Sie können einfach den folgenden Code aufrufen, um eine Antwort zu erhalten:

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

Hier ist der vollständige Arbeitscode :

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

Vielen Dank :)

Md. Sajedul Karim
quelle
2
'org.springframework.web.client.HttpClientErrorException' ist eine Unterklasse von 'org.springframework.web.client.HttpStatusCodeException'. Sie müssen HttpClientErrorException nicht abfangen, wenn Sie bereits HttpStatusCodeException abfangen und bei der Behandlung der beiden oben genannten Ausnahmen nichts anderes tun.
The-Proton-Resurgence
0

Eine sehr einfache Lösung kann sein:

try {
     requestEntity = RequestEntity
     .get(new URI("user String"));
    
    return restTemplate.exchange(requestEntity, String.class);
} catch (RestClientResponseException e) {
        return ResponseEntity.status(e.getRawStatusCode()).body(e.getResponseBodyAsString());
}
Vaibhav Sharma
quelle
-1

Hier ist meine POST-Methode mit HTTPS, die einen Antworttext für jede Art von fehlerhaften Antworten zurückgibt.

public String postHTTPSRequest(String url,String requestJson)
{
    //SSL Context
    CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    //Initiate REST Template
    RestTemplate restTemplate = new RestTemplate(requestFactory);
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    //Send the Request and get the response.
    HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
    ResponseEntity<String> response;
    String stringResponse = "";
    try {
        response = restTemplate.postForEntity(url, entity, String.class);
        stringResponse = response.getBody();
    }
    catch (HttpClientErrorException e)
    {
        stringResponse = e.getResponseBodyAsString();
    }
    return stringResponse;
}
vkrams
quelle
3
Sie müssen einen anderen HttpClient als das Standard-SDK verwenden, um den Antworttext für Fehler zu erhalten (z. B. Apache Commons HttpClient)
Rasiermesser