Yay Yeniden Şablonu istisna işleme


124

Kod parçacığı aşağıdadır; Temel olarak, hata kodu 200'den başka bir şey olduğunda istisnayı yaymaya çalışıyorum.

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

Ancak sunucudan 500 yanıt alınması durumunda istisna alıyorum

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]

Geri kalan şablon değişim yöntemini denemede gerçekten sarmam gerekiyor mu? O halde kodların amacı ne olabilir?


Lütfen ApplicationException () kodunu paylaşın
Mudassar

Yanıtlar:


128

ResponseErrorHandlerGeriye kalan şablonunuzun hata işlemesini ayarlamak için uygulayan bir sınıf oluşturmak ve ardından bunun bir örneğini kullanmak istiyorsunuz:

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

Ayrıca Spring, DefaultResponseErrorHandleryalnızca handleErroryöntemi geçersiz kılmak istemeniz durumunda arabirimi uygulamak yerine genişletebileceğiniz sınıfa sahiptir .

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

Spring'in HTTP hatalarını nasıl işlediğine dair bir fikir edinmek için kaynak koduna bir göz atın .


1
Farklı çağrılar için yeniden kullandığım 1 RestTemplate örneğim var. Farklı çağrılardan gelen hataları farklı şekilde ele almam gerekiyor - görünüşe göre bunu global işleyiciyle yapmanın bir yolu yok - istek başına bir işleyici sağlamam gerekiyor.
mvmn

4
Bu hata işleyiciyle her zaman a alırım ResourceAccessExceptionçünkü s'yi RestTemplate.doExecuteyakalar IOExceptionve a atar ResourceAccessException. Neyi yanlış yapıyorum?
Federico Bellucci

DefaultResponseErrorHandler'ı genişleterek bunu çözebildim.
Crenguta S

48

Bir HttpStatusCodeExceptionistisna yakalamalısın :

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

37
IMO yanıtı her zaman uygun bir durum koduyla birlikte gelmelidir, aksi takdirde kodların amacı ne olur.
vaibhav

5
@Vaibhav itirazını anlayacağımdan emin değilim: HttpStatusCodeException'ı yakalamak yanlış bir kod için değildir, ancak çoğu durumda bir istisna atıldığı ve bu nedenle if (kod == değeri) asla çalıştırılamayacağı için.
Stefano Scarpanti

1
Java'da istisnalar çok maliyetlidir. Ara sıra ortaya çıkan beklenmedik nedenler için sorun yok (dolayısıyla adı), ancak bunun üzerine başka çözümler aramalısınız.
Agoston Horvath

11
"Çok pahalı" mı? Diyelim ki bir HTTP çağrısı yapmaktan daha mı maliyetli?
IcedDante

4
@RaffaelBecharaRameh - HttpStatusCodeException .getResponseBodyAsString () veya HttpStatusCodeException.getResponseBodyAsByteArray ().
Dave

45

Spring, http hata kodlarını akıllıca istisna olarak ele alır ve istisna işleme kodunuzun hatayı ele alacak bağlama sahip olduğunu varsayar. Borsanın beklediğiniz gibi çalışması için şunu yapın:

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

Bu, yanıttan beklenen tüm sonuçları döndürecektir.


2
Hatalar için yanıt gövdesi almak için varsayılan SDK'dan farklı HttpClient kullanmanız gerekir
jilet

26

Başka bir çözüm, bu yazının sonunda "enlian" tarafından açıklanan çözümdür: 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
Hatalar için yanıt gövdesini almak için varsayılan SDK'dan farklı HttpClient kullanmanız gerekir (örneğin apache commons HttpClient)
jilet

17

Bahar sizi çok çok büyük http durum kodu listesinden özetler. İstisnalar fikri budur. Org.springframework.web.client.RestClientException hiyerarşisine bir göz atın:

Http yanıtlarıyla uğraşırken en sık karşılaşılan durumların haritasını çıkarabileceğiniz bir dizi sınıfınız var. Http kodları listesi gerçekten büyüktür, her durumu ele almak için kod yazmak istemezsiniz. Ancak örneğin, HttpClientErrorException alt hiyerarşisine bir göz atın. Herhangi bir 4xx tipi hatayı eşlemek için tek bir istisnanız var. Derine gitmen gerekiyorsa, yapabilirsin. Ancak yalnızca HttpClientErrorException'ı yakalayarak, hizmete kötü verilerin sağlandığı her durumu halledebilirsiniz.

DefaultResponseErrorHandler gerçekten basit ve sağlamdır. Yanıt durum kodu 2xx ailesinden değilse, hasError yöntemi için yalnızca true döndürür.


Dostum, açıklama için teşekkürler. Bu ağacı istisna hiyerarşisiyle nasıl oluşturdunuz?
tek başına

1
Dostum, Eclipse kullandım. Tür aramayı açmak için control + shift + T tuşlarına basın ve RestClientException yazın. Sonuçlardan RestClientException üzerine çift tıklayın, Eclipse o sınıfı sizin için açacaktır. Ardından, fare imlecini sınıf adının üzerine getirin (burada "genel sınıf RestClientException ..." yazıyor ve control + T'ye basın. Bu hiyerarşiyi göreceksiniz.
Perimosh

onu denedin mi?
Perimosh

1
Intellij'de BTW: proje ağacındaki sınıfa tıklayın ve Ctrl + Alt + U veya sağ tıklayın -> Diyagram oluştur
bağımsız

3

Sizinle birlikte havuzlama (http istemci fabrikası) veya yük dengeleme (eureka) mekanizması kullanırsanız RestTemplate, new RestTemplatesınıf başına bir oluşturma lüksüne sahip olmayacaksınız . Birden fazla hizmeti arıyorsanız, kullanamazsınız setErrorHandlerçünkü tüm istekleriniz için global olarak kullanılır.

Bu durumda, yakalamak HttpStatusCodeExceptiondaha iyi bir seçenek gibi görünüyor.

Sahip olduğunuz diğer tek seçenek RestTemplate, @Qualifieraçıklamayı kullanarak birden çok örneği tanımlamaktır .

Ayrıca - ama bu kendi zevkim - hata işlememin çağrılarıma sıkı sıkıya sarılmasını seviyorum.


3

Bunu aşağıdaki gibi hallettim:

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

1

Değişim kodu aşağıdadır :

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

İstisna RestClientExceptionvardır HttpClientErrorExceptionve HttpStatusCodeExceptionistisnadır.

Yani RestTempleteorada meydana gelebilir HttpClientErrorExceptionve HttpStatusCodeExceptionistisna olabilir . İstisna nesnesinde şu şekilde tam hata mesajı alabilirsiniz:exception.getResponseBodyAsString()

İşte örnek kod :

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

İşte kod açıklaması :

Bu yöntemde istek ve yanıt sınıfını geçmeniz gerekir. Bu yöntem, yanıtı otomatik olarak istenen nesne olarak ayrıştırır.

Her şeyden önce mesaj dönüştürücü eklemelisiniz.

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

O zaman eklemelisin requestHeader. İşte kod:

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

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

Son olarak, değişim yöntemini çağırmanız gerekir:

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

Prety baskı için Gson kütüphanesini kullandım. işte gradle:compile 'com.google.code.gson:gson:2.4'

Yanıt almak için aşağıdaki kodu aramanız yeterlidir:

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

İşte tam çalışma kodu :

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

Teşekkürler :)


2
'org.springframework.web.client.HttpClientErrorException', 'org.springframework.web.client.HttpStatusCodeException' alt sınıfıdır. HttpStatusCodeException'ı zaten yakalıyorsanız ve yukarıdaki iki özel durumu ele alırken farklı bir şey yapmıyorsanız, HttpClientErrorException'ı yakalamanıza gerek yoktur.
The-Proton-Resurgence

0

Çok basit bir çözüm şunlar olabilir:

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

-1

İşte her tür kötü yanıt için bir yanıt gövdesi döndüren HTTPS'li POST yöntemim.

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

3
Hatalar için yanıt gövdesini almak için varsayılan SDK'dan farklı HttpClient kullanmanız gerekir (örneğin apache commons HttpClient)
jilet
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.