Обработка исключений Spring Resttemplate


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]

Мне действительно нужно оборачивать остальные методы обмена шаблонами в try? В чем тогда заключалась бы цель кодов?


Пожалуйста, поделитесь кодом 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
С этим обработчиком ошибок я всегда получаю, ResourceAccessExceptionпотому что RestTemplate.doExecuteловит IOExceptions и выбрасывает ResourceAccessException. Что я делаю не так?
Федерико Беллуччи

Я смог решить эту проблему, расширив DefaultResponseErrorHandler.
Crenguta S

48

Вы должны поймать HttpStatusCodeExceptionисключение:

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

37
ИМО ответ всегда должен сопровождаться соответствующим кодом состояния, иначе какова цель кодов.
vaibhav

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

1
Исключения в Java обходятся очень дорого. Это нормально для случайных, неожиданных причин (отсюда и название), но вместо этого вам следует искать другие решения.
Agoston Horvath

11
"Очень дорого"? Дороже, чем, скажем, HTTP-вызов?
IcedDante 06

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 по умолчанию, чтобы получить тело ответа об ошибках
razor

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

Spring абстрагирует вас от очень-очень большого списка кодов состояния http. В этом суть исключений. Взгляните на иерархию org.springframework.web.client.RestClientException:

У вас есть несколько классов для отображения наиболее распространенных ситуаций при работе с HTTP-ответами. Список http-кодов действительно велик, вам не нужно писать код для обработки каждой ситуации. Но, например, взгляните на подиерархию HttpClientErrorException. У вас есть единственное исключение для отображения любой ошибки типа 4xx. Если вам нужно углубиться, то можете. Но просто перехватив HttpClientErrorException, вы можете справиться с любой ситуацией, когда в службу были предоставлены неверные данные.

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


Чувак, спасибо за объяснение. Как вы построили это дерево с иерархией исключений?
стоять в покое

1
Привет, парень, я использовал Eclipse. Просто нажмите Ctrl + Shift + T, чтобы открыть средство поиска типов, и введите RestClientException. Дважды щелкните RestClientException в результатах, Eclipse откроет этот класс для вас. Затем наведите курсор мыши на имя класса (где написано «public class RestClientException ...» и нажмите Ctrl + T. Вы увидите эту иерархию.
Perimosh

ты пробовал?
Перимош

1
Кстати, в Intellij это: щелкните класс в дереве проекта и нажмите Ctrl + Alt + U, или щелкните правой кнопкой мыши -> Построить диаграмму
отдельно

3

Если вы используете механизм объединения (фабрика http-клиента) или балансировки нагрузки (эврика) с вашим RestTemplate, у вас не будет роскоши создать отдельный new RestTemplateкласс. Если вы вызываете более одной службы, вы не можете использовать ее, setErrorHandlerпотому что if будет использоваться глобально для всех ваших запросов.

В этом случае 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 и ничего не делаете по-другому при обработке двух вышеуказанных исключений.
The-Proton-Resurgence

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.