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]

本当に残りのテンプレート交換メソッドをラップする必要がありますか?コードの目的は何でしょうか?


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ます。何が悪いのですか?
フェデリコベルッチ

DefaultResponseErrorHandlerを拡張することでこれを解決できました。
Crenguta S 2018

48

HttpStatusCodeException例外をキャッチする必要があります:

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

37
IMOの応答には常に適切なステータスコードが含まれている必要があります。
vaibhav

5
@vaibhavの反対意見を理解できるかわかりません。HttpStatusCodeExceptionをキャッチすることは間違ったコードではありませんが、多くの場合、例外が常にスローされるため、if(code == value)は実行できません。
Stefano Scarpanti 2017

1
例外は、Javaでは非常にコストがかかります。偶発的な予期しない原因(したがって名前)は問題ありませんが、それ以上に、代わりに他の解決策を探す必要があります。
Agoston Horvath 2017年

11
「とても高い」?たとえば、HTTP呼び出しを行うよりもコストがかかりますか?
IcedD​​ante 2018年

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)
かみそり

17

Springは、非常に大規模なhttpステータスコードのリストからユーザーを抽象化します。それが例外の考え方です。org.springframework.web.client.RestClientException階層を調べます。

http応答を処理する際の最も一般的な状況をマッピングするための一連のクラスがあります。httpコードのリストは非常に大きく、それぞれの状況を処理するコードを記述したくありません。しかし、たとえば、HttpClientErrorExceptionサブ階層を見てみましょう。4xxの種類のエラーをマップするための単一の例外があります。深く掘り下げる必要がある場合は、可能です。しかし、HttpClientErrorExceptionをキャッチするだけで、サービスに不正なデータが提供された状況を処理できます。

DefaultResponseErrorHandlerは本当にシンプルでしっかりしています。応答ステータスコードが2xxファミリのものではない場合、hasErrorメソッドに対してtrueを返すだけです。


おい、説明ありがとう。例外階層を使用してこのツリーをどのように構築しましたか?
スタンドアロン

1
ねえ、私はEclipseを使いました。control + shift + Tを押してタイプサーチャーを開き、RestClientExceptionと入力します。結果からRestClientExceptionをダブルクリックすると、Eclipseがそのクラスを開きます。その後、クラス名(それが言う「パブリッククラスRestClientExceptionを...」、およびプレス制御+ Tの上にマウスカーソルを置くあなたは、その階層が表示されます。。
Perimosh

やってみましたか?
ペリモッシュ

1
Intellijでそれは次のとおりです。プロジェクトツリーでクラスをクリックしてCtrl + Alt + U、または右マウスクリック->ビルドダイアグラム
スタンドアロン

3

でプーリング(httpクライアントファクトリ)またはロードバランシング(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);

最後に、exchangeメソッドを呼び出す必要があります。

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

きれいな印刷には、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をキャッチしていて、上記の2つの例外の処理で何も変わっていない場合は、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)
かみそり
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.