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
다른 호출에 재사용하는 RestTemplate 인스턴스가 1 개 있습니다. 다른 호출의 오류를 다르게 처리해야합니다. 전역 처리기로는이를 수행 할 수있는 방법이 없습니다. 요청마다 처리기를 제공해야합니다.
mvmn

4
이 오류 핸들러로 난 항상 얻을 수 ResourceAccessException있기 때문에 RestTemplate.doExecute어획량 IOException들과 던졌습니다 ResourceAccessException. 내가 뭘 잘못하고 있죠?
Federico Bellucci

DefaultResponseErrorHandler를 확장하여이 문제를 해결할 수있었습니다.
Crenguta S

48

HttpStatusCodeException예외를 포착해야합니다 .

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

37
IMO 응답은 항상 적절한 상태 코드와 함께 제공되어야하며 그렇지 않은 경우 코드의 목적은 무엇입니까?
VAIBHAV

5
@vaibhav 이의 제기를 이해할 수 없습니다. HttpStatusCodeException을 잡는 것은 잘못된 코드가 아니지만 많은 경우 예외가 항상 발생하므로 if (code == value)를 실행할 수 없기 때문입니다.
스테파노 Scarpanti

1
예외는 Java에서 매우 비용이 많이 듭니다. 가끔 예상치 못한 원인 (따라서 이름)에 대해서는 괜찮지 만 그 대신 다른 해결책을 찾아야합니다.
Agoston Horvath

11
"매우 비싸다"? 예를 들어, HTTP 호출보다 비용이 많이 듭니까?
IcedDante

4
@RaffaelBecharaRameh-HttpStatusCodeException .getResponseBodyAsString () 또는 HttpStatusCodeException.getResponseBodyAsByteArray ().
Dave

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
당신은 오류에 대한 응답 본문을 얻기 위해, 기본 SDK과 다른 HttpClient를를 사용할 필요가
면도칼

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
오류에 대한 응답 본문을 얻으려면 기본 SDK와 다른 HttpClient를 사용해야합니다 (예 : apache commons HttpClient)
razor

17

Spring은 매우 큰 http 상태 코드 목록에서 당신을 추상화합니다. 그것이 예외의 개념입니다. org.springframework.web.client.RestClientException 계층 구조를 살펴보십시오.

http 응답을 처리 할 때 가장 일반적인 상황을 매핑하는 많은 클래스가 있습니다. http 코드 목록은 정말 크므로 각 상황을 처리하는 코드를 작성하고 싶지 않을 것입니다. 그러나 예를 들어 HttpClientErrorException 하위 계층 구조를 살펴보십시오. 4xx 종류의 오류를 매핑하는 단일 예외가 있습니다. 깊이 들어가야한다면 할 수 있습니다. 그러나 HttpClientErrorException을 포착하기 만하면 서비스에 잘못된 데이터가 제공된 모든 상황을 처리 할 수 ​​있습니다.

DefaultResponseErrorHandler는 정말 간단하고 견고합니다. 응답 상태 코드가 2xx 계열이 아닌 경우 hasError 메서드에 대해 true를 반환합니다.


야, 설명 감사합니다. 예외 계층 구조로이 트리를 어떻게 구축 했습니까?
혼자서

1
안녕하세요, 저는 Eclipse를 사용했습니다. Ctrl + Shift + T를 눌러 유형 검색기를 열고 RestClientException을 입력하십시오. 결과에서 RestClientException을 두 번 클릭하면 Eclipse가 해당 클래스를 엽니 다. 그런 다음 마우스 커서를 클래스 이름 ( "public class RestClientException ..."이라고 표시된 곳) 위에 놓고 control + T를 누릅니다. 해당 계층이 표시됩니다.
Perimosh

해봤 어?
Perimosh

1
Intellij에서 Btw는 다음과 같습니다. 프로젝트 트리에서 클래스를 클릭하고 Ctrl + Alt + U 또는 오른쪽 마우스 클릭-> 다이어그램 작성
독립형

3

에서 풀링 (http 클라이언트 팩토리) 또는로드 밸런싱 (유레카) 메커니즘을 사용 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

예외 RestClientExceptionHttpClientErrorException있고 HttpStatusCodeException예외가 있습니다.

그렇게 RestTemplete이 발생 시킬수 있습니다 HttpClientErrorExceptionHttpStatusCodeException예외입니다. 예외 객체에서 다음과 같은 방법으로 정확한 오류 메시지를 얻을 수 있습니다.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);

마지막으로 exchange 메소드를 호출해야합니다.

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

prety 인쇄를 위해 Gson 라이브러리를 사용했습니다. 여기 gradle이 있습니다.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'의 하위 클래스입니다. 이미 HttpStatusCodeException을 포착하고 위의 두 예외를 처리하는 데 아무런 차이가없는 경우 HttpClientErrorException을 포착 할 필요가 없습니다.
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

다음은 모든 유형의 잘못된 응답에 대한 응답 본문을 반환하는 HTTPS를 사용하는 내 POST 메서드입니다.

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
오류에 대한 응답 본문을 얻으려면 기본 SDK와 다른 HttpClient를 사용해야합니다 (예 : apache commons HttpClient)
razor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.