Обробка виключень з весняного реструктуризації


124

Нижче - фрагмент коду; в основному, я намагаюся поширити виняток, коли код помилки - це щось інше, ніж 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();
            }

Однак у випадку 500 відповіді від сервера я отримую виняток

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]

Чи дійсно мені потрібно перетворити метод обміну шаблонів у спробі? Яка б тоді була мета кодів?


Будь ласка, поділіться кодом ApplicationException ()
Mudassar

Відповіді:


128

Ви хочете створити клас, який реалізує, ResponseErrorHandlerа потім використовувати його примірник, щоб встановити обробку помилок вашого шабло відпочинку:

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

Також у Spring є клас DefaultResponseErrorHandler, який ви можете розширити замість реалізації інтерфейсу, якщо ви хочете лише замінити handleErrorметод.

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

Погляньте на його вихідний код, щоб мати уявлення про те, як Spring обробляє помилки HTTP.


1
У мене є 1 екземпляр RestTemplate, який я повторно використовую для різних дзвінків. Мені потрібно по-різному обробляти помилки з різних дзвінків - мабуть, немає можливості зробити це з глобальним обробником - мені потрібно надати обробник на запит.
mvmn

4
З цим обробником помилок я завжди отримую a, ResourceAccessExceptionтому що RestTemplate.doExecuteловить IOExceptions і кидає ResourceAccessException. Що я роблю неправильно?
Федеріко Беллучі

Мені вдалося вирішити це, розширивши DefaultResponseErrorHandler.
Кренгута S

48

Ви повинні зловити HttpStatusCodeExceptionвиняток:

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

37
Відповідь ІМО завжди має відповідати коду статусу, інакше яка мета коду.
vaibhav

5
Я не впевнений, що розумію заперечення @vaibhav: ловити HttpStatusCodeException не для неправильного коду, а тому, що у багатьох випадках виняток завжди викидається, тому ваш if (code == value) ніколи не може бути виконаний.
Стефано Скарпанті

1
Винятки в Java дуже дорогі. Це нормально для випадкових, несподіваних причин (звідси і назва), але над цим слід шукати інші рішення.
Агостон Горват

11
"Дуже дорого"? Дорожче, ніж, скажімо, здійснення HTTP-дзвінка?
IcedDante

4
@RaffaelBecharaRameh - HttpStatusCodeException .getResponseBodyAsString () або HttpStatusCodeException.getResponseBodyAsByteArray ().
Дейв

45

Spring спритно розглядає коди помилок http як винятки та передбачає, що ваш код обробки винятків має контекст для обробки помилки. Щоб обмін функціонував так, як ви цього очікували, зробіть це:

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

Це поверне всі очікувані результати від відповіді.


2
вам потрібно використовувати інший HttpClient, ніж SDK за замовчуванням, щоб отримати тіло відповіді на помилки
бритвою

26

Ще одне рішення - це описане тут в кінці цього допису "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
вам потрібно використовувати інший HttpClient, ніж SDK за замовчуванням, щоб отримати тіло відповідей на помилки (наприклад, apache commons HttpClient)
бритва

17

Весна резюмує вас з дуже-дуже великого списку коду статусу http. Така ідея винятків. Погляньте на ієрархію org.springframework.web.client.RestClientException:

У вас є купа занять, щоб зіставити найпоширеніші ситуації при роботі з http-відповідями. Список http-кодів дійсно великий, ви не хочете писати код для обробки кожної ситуації. Але, наприклад, загляньте в підієрархію HttpClientErrorException. У вас є єдиний виняток, щоб відобразити будь-яку помилку 4xx. Якщо вам потрібно заглибитись, то можете. Але лише зафіксувавши HttpClientErrorException, ви можете впоратися з будь-якою ситуацією, коли в службу були надані погані дані.

DefaultResponseErrorHandler дійсно простий і надійний. Якщо код статусу відповіді не належить до сімейства 2xx, він просто повертає true для методу hasError.


Чувак, дякую за пояснення. Як ви створили це дерево за ієрархією винятків?
самостійно

1
Ей, чоловіче, я використовував Eclipse. Просто натисніть клавішу управління + shift + T, щоб відкрити пошук типу, і введіть RestClientException. Двічі клацніть на RestClientException з результатів, Eclipse відкриє цей клас для вас. Потім наведіть курсор миші на назву класу (там, де написано "загальнодоступний клас RestClientException ...", і натисніть клавішу управління + Т. Ви побачите цю ієрархію.
Перімош,

ти спробував?
Перімош

1
Btw в Intellij це: клацніть по класу в дереві проектів і Ctrl + Alt + U, або клацніть правою кнопкою миші -> Діаграма побудови
самостійно

3

Якщо ви використовуєте механізм об'єднання (http client factory) або завантажуєте механізм балансування (eureka) зі своїм RestTemplate, вам не буде розкішно створювати для new RestTemplateкожного класу. Якщо ви зателефонували більше ніж одну службу, ви не можете скористатися, setErrorHandlerоскільки якщо б вона була використана у всьому світі для всіх ваших запитів.

У цьому випадку HttpStatusCodeExceptionкращий варіант, здається, є кращим.

Єдиний інший варіант, який ви маєте, - це визначити кілька RestTemplateпримірників за допомогою @Qualifierпримітки.

Також - але це - мій власний смак - мені подобається, що моя помилка обробляється щільно до моїх дзвінків.


3

Я вирішив це як нижче:

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

Код обміну нижче :

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

Виняток RestClientExceptionмає HttpClientErrorExceptionі HttpStatusCodeExceptionвиняток.

Тож RestTempleteтам може статися HttpClientErrorExceptionі HttpStatusCodeExceptionвиняток. За винятком об'єкта, ви можете отримати точне повідомлення про помилку, використовуючи цей спосіб:exception.getResponseBodyAsString()

Ось приклад коду :

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

Ось код опису :

У цьому методі ви повинні пройти клас запиту та відповіді. Цей метод автоматично розбере відповідь як запитуваний об'єкт.

Перш за все вам потрібно додати конвертер повідомлень.

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

Тоді вам доведеться додати requestHeader. Ось код:

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

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

Нарешті, ви повинні зателефонувати за методом обміну:

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

Для друку доти я використовував бібліотеку Gson. ось градель:compile 'com.google.code.gson:gson:2.4'

Ви можете просто зателефонувати нижче, щоб отримати відповідь:

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

Ось повний робочий код :

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

Дякую :)


2
'org.springframework.web.client.HttpClientErrorException' - підклас 'org.springframework.web.client.HttpStatusCodeException'. Вам не потрібно ловити HttpClientErrorException, якщо ви вже ловили HttpStatusCodeException і нічого не робите в обробці вищевказаних двох винятків.
Протон-Відродження

0

Дуже простим рішенням може бути:

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

Ось мій метод POST з HTTPS, який повертає тіло відповідей на будь-який тип поганих реакцій.

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
вам потрібно використовувати інший HttpClient, ніж SDK за замовчуванням, щоб отримати тіло відповідей на помилки (наприклад, apache commons HttpClient)
бритва
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.