Penanganan pengecualian Spring Resttemplate

124

Di bawah ini adalah potongan kode; pada dasarnya, saya mencoba menyebarkan pengecualian ketika kode kesalahan adalah selain 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();
            }

Namun dalam kasus respon 500 dari server saya mendapatkan pengecualian

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]

Apakah saya benar-benar perlu membungkus metode pertukaran template lainnya dalam percobaan? Lalu, apa tujuan kode?

vaibhav.dll
sumber
Mohon bagikan kode ApplicationException ()
Mudassar

Jawaban:

128

Anda ingin membuat kelas yang mengimplementasikan ResponseErrorHandlerdan kemudian menggunakan sebuah instance untuk mengatur penanganan kesalahan dari template lainnya:

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

Selain itu, Spring memiliki kelas DefaultResponseErrorHandler, yang dapat Anda perluas alih-alih mengimplementasikan antarmuka, jika Anda hanya ingin mengganti handleErrormetode.

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

Lihatlah kode sumbernya untuk mendapatkan gambaran tentang bagaimana Spring menangani kesalahan HTTP.

carcaret
sumber
1
Saya memiliki 1 contoh RestTemplate yang saya gunakan kembali untuk panggilan yang berbeda. Saya perlu menangani kesalahan dari panggilan yang berbeda secara berbeda - tampaknya tidak ada cara untuk melakukannya dengan penangan global - saya perlu memberikan penangan per permintaan.
mvmn
4
Dengan penanganan kesalahan ini saya selalu mendapatkan ResourceAccessExceptionkarena RestTemplate.doExecutemenangkap IOExceptiondan melempar ResourceAccessException. Apa yang saya lakukan salah?
Federico Bellucci
Saya dapat menyelesaikan ini dengan memperluas DefaultResponseErrorHandler.
Crenguta S
48

Anda harus menangkap HttpStatusCodeExceptionpengecualian:

try {
    restTemplate.exchange(...);
} catch (HttpStatusCodeException exception) {
    int statusCode = exception.getStatusCode().value();
    ...
}
mekazu
sumber
37
IMO respon harus selalu datang dengan kode status yang sesuai, jika tidak apa tujuan kode.
vaibhav
5
Saya tidak yakin untuk memahami keberatan @vaibhav: menangkap HttpStatusCodeException bukan untuk kode yang salah, tetapi karena dalam banyak kasus pengecualian selalu dilemparkan sehingga if (code == value) Anda tidak akan pernah dapat dijalankan.
Stefano Scarpanti
1
Pengecualian sangat mahal di Jawa. Tidak apa-apa untuk penyebab yang kadang-kadang tidak terduga (karena itu namanya), tetapi lebih dari itu, Anda harus mencari solusi lain.
Agoston Horvath
11
"Sangat mahal"? Lebih mahal daripada, katakanlah, melakukan panggilan HTTP?
IcedDante
4
@RaffaelBecharaRameh - HttpStatusCodeException .getResponseBodyAsString () atau HttpStatusCodeException.getResponseBodyAsByteArray ().
Dave
45

Spring dengan cerdik memperlakukan kode kesalahan http sebagai pengecualian, dan mengasumsikan bahwa kode penanganan pengecualian Anda memiliki konteks untuk menangani kesalahan tersebut. Agar pertukaran berfungsi seperti yang Anda harapkan, lakukan ini:

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

Ini akan mengembalikan semua hasil yang diharapkan dari respons.

austin cherlo
sumber
2
Anda perlu menggunakan HttpClient yang berbeda dari SDK default, untuk mendapatkan isi respons atas kesalahan
razor
26

Solusi lain adalah yang dijelaskan di sini di akhir posting ini oleh "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 
}
Peneliti
sumber
4
Anda perlu menggunakan HttpClient yang berbeda dari SDK default, untuk mendapatkan isi respons untuk kesalahan (misalnya apache commons HttpClient)
razor
17

Spring memisahkan Anda dari daftar kode status http yang sangat besar. Itulah gagasan tentang pengecualian. Lihat hierarki org.springframework.web.client.RestClientException:

Anda memiliki banyak kelas untuk memetakan situasi paling umum saat menangani tanggapan http. Daftar kode http sangat besar, Anda tidak ingin menulis kode untuk menangani setiap situasi. Tapi misalnya, lihat sub-hierarki HttpClientErrorException. Anda memiliki satu pengecualian untuk memetakan semua jenis kesalahan 4xx. Jika Anda perlu mendalami, maka Anda bisa. Namun hanya dengan menangkap HttpClientErrorException, Anda dapat menangani situasi apa pun saat data buruk diberikan ke layanan.

DefaultResponseErrorHandler sangat sederhana dan solid. Jika kode status respons bukan dari keluarga 2xx, itu hanya mengembalikan true untuk metode hasError.

Perimosh
sumber
Bung, terima kasih atas penjelasannya. Bagaimana Anda membangun pohon ini dengan hierarki pengecualian?
berdiri sendiri
1
Halo, saya menggunakan Eclipse. Cukup tekan control + shift + T untuk membuka pencari jenis, dan ketik RestClientException. Klik dua kali pada RestClientException dari hasil, Eclipse akan membuka kelas itu untuk Anda. Kemudian, letakkan kursor mouse di atas nama kelas (di mana dikatakan "kelas publik RestClientException ...", dan tekan control + T. Anda akan melihat hierarki itu.
Perimosh
Apakah kamu sudah mencobanya?
Perimosh
1
Btw di Intellij itu adalah: klik pada kelas di pohon proyek dan Ctrl + Alt + U, atau klik kanan mouse -> Bangun diagram
berdiri sendiri
3

Jika Anda menggunakan mekanisme pooling (http client factory) atau load balancing (eureka) dengan Anda RestTemplate, Anda tidak akan memiliki kemewahan membuat new RestTemplateper kelas. Jika Anda memanggil lebih dari satu layanan, Anda tidak dapat menggunakan setErrorHandlerkarena jika akan digunakan secara global untuk semua permintaan Anda.

Dalam hal ini, menangkap HttpStatusCodeExceptionnampaknya merupakan pilihan yang lebih baik.

Satu-satunya pilihan lain yang Anda miliki adalah menentukan beberapa RestTemplatecontoh menggunakan @Qualifieranotasi.

Juga - tetapi ini adalah selera saya sendiri - saya suka penanganan kesalahan saya meringkuk erat ke panggilan saya.

Hannes
sumber
3

Saya telah menangani ini seperti di bawah ini:

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
sumber
1

Kode pertukarannya di bawah ini :

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

Pengecualian RestClientExceptionmemiliki HttpClientErrorExceptiondan HttpStatusCodeExceptionpengecualian.

Jadi di RestTempletesana mungkin terjadi HttpClientErrorExceptiondan HttpStatusCodeExceptionpengecualian. Dalam objek pengecualian Anda bisa mendapatkan pesan kesalahan yang tepat menggunakan cara ini:exception.getResponseBodyAsString()

Berikut ini contoh kode :

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

Berikut adalah deskripsi kodenya :

Dalam metode ini Anda harus melewati kelas permintaan dan respons. Metode ini akan secara otomatis mengurai respons sebagai objek yang diminta.

Pertama-tama Anda harus menambahkan konverter pesan.

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

Kemudian Anda harus menambahkan requestHeader. Ini kodenya:

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

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

Akhirnya, Anda harus memanggil metode pertukaran:

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

Untuk pencetakan prety saya menggunakan perpustakaan Gson. ini gradle:compile 'com.google.code.gson:gson:2.4'

Anda bisa memanggil kode di bawah ini untuk mendapatkan respon:

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

Ini kode lengkapnya :

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

Terima kasih :)

Md. Sajedul Karim
sumber
2
'org.springframework.web.client.HttpClientErrorException' adalah subclass dari 'org.springframework.web.client.HttpStatusCodeException'. Anda tidak perlu menangkap HttpClientErrorException jika Anda sudah menangkap HttpStatusCodeException dan tidak melakukan apa pun yang berbeda dalam menangani dua pengecualian di atas.
The-Proton-Resurgence
0

Solusi yang sangat sederhana dapat berupa:

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
sumber
-1

Berikut adalah metode POST saya dengan HTTPS yang mengembalikan isi respons untuk semua jenis respons buruk.

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
sumber
3
Anda perlu menggunakan HttpClient yang berbeda dari SDK default, untuk mendapatkan isi respons untuk kesalahan (misalnya apache commons HttpClient)
razor