Help us understand the problem. What is going on with this article?

【Spring】RestTemplateが投げる例外クラスまとめ

More than 1 year has passed since last update.

概要

SpringのRestTemplateを使ったAPIコールにおいて、エラー時に投げられる例外クラスの種類をまとめた。
背景としては、SpringのRestTemplateを使っていて、これからエラー時の処理を実装するにあたって必要だと思ったので。

調査方法としては、RestTemplateのソースコードを漁ってみて、例外クラスのjavadocを見たり、実際にthrowされている箇所を見たりしてわかったことをまとめた。

環境

spring-web-4.3.16.RELEASE

例外クラスまとめ

RestTemplateの中には全部で7種類の例外が定義されていた。
それらをクラス図っぽくしてみたのが下記。

RestTemplate (2).png

ひとつずつ見てみる。

RestClientException

javadocは以下

/**
 * Base class for exceptions thrown by {@link RestTemplate} whenever it encounters
 * client-side HTTP errors.
 */
public class RestClientException extends NestedRuntimeException {

RestTemplateの実行において、エラーが発生したときにRestTemplateが投げるベースの例外クラス。
RestTemplateに関する例外全ての親。

RestClientExceptionはNestedRuntimeExceptionを継承しているが、RestTemplateの話からはずれるのでここでは取り上げない。

ResourceAccessException

javadocは以下

/**
 * Exception thrown when an I/O error occurs.
 */
public class ResourceAccessException extends RestClientException {

IO系のエラーが発生したら投げられる例外クラス。
コードを見た感じでは、リクエスト時にIOExceptionが投げられたらcatchしてResourceAccessExceptionをthrowしてるっぽい。

RestClientResponseException

javadocは以下

/**
 * Common base class for exceptions that contain actual HTTP response data.
 */
public class RestClientResponseException extends RestClientException {

HTTPのレスポンスに関する例外を扱うクラス。
どうやら下記で述べるUnknownHTTPStatusCodeExceptionHTTPStatusCodeExceptionに継承されているだけで、このクラスが名指しでthrowされることは無さそうで、だったら抽象クラスでもいいんじゃ?と思いましたが、どうなんでしょう。

UnknownHTTPStatusCodeException

javadocは以下

/**
 * Exception thrown when an unknown (or custom) HTTP status code is received.
 */
public class UnknownHttpStatusCodeException extends RestClientResponseException {

未知のHTTPステータスコードが返されたときに投げられる。
具体的には、返されたステータスコードが、org.springframework.http.HttpStatusのenumで定義されているステータスコード達の中に含まれていなかったら投げられる。

HTTPStatusCodeException

javadocは以下

/**
 * Abstract base class for exceptions based on an {@link HttpStatus}.
 */
public abstract class HttpStatusCodeException extends RestClientResponseException {

HTTPのレスポンスのステータスコードに関するException。
これは抽象クラスで、下記で述べるHTTPServerErrorExceptionHTTPClientErrorExceptionに継承されている。

HTTPServerErrorException

javadocは以下

/**
 * Exception thrown when an HTTP 5xx is received.
 */
public class HttpServerErrorException extends HttpStatusCodeException {

5xx系のステータスコードが返されたときに投げられる。

HTTPClientErrorException

javadocは以下

/**
 * Exception thrown when an HTTP 4xx is received.
 */
public class HttpClientErrorException extends HttpStatusCodeException {

4xx系のステータスコードが返されたときに投げられる。

エラーハンドリングの方針

以上のルールを頭に入れたうえで、catchしたい例外をcatchすればよさそう。
RestTemplateに関する例外を全てcatchしたければ、RestClientExceptionをcatchすればよい。

おまけ:エラー系のステータスコードが返ったときの挙動を少し追ってみる

まずエラーかどうかの判定するのが下記のメソッド
ステータスコードが4xx系, もしくは5xx系だったらtrueが返るみたい
未知のステータスコードの場合、つまりorg.springframework.http.HttpStatusのenumで定義されているステータスコード達の中に含まれていなかったら、getHttpStatusCode()の中でUnknownHTTPStatusCodeExceptionが投げられる。

@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
    try {
        return hasError(getHttpStatusCode(response));
    }
    catch (UnknownHttpStatusCodeException ex) {
        return false;
    }
}

protected boolean hasError(HttpStatus statusCode) {
    return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR ||
                statusCode.series() == HttpStatus.Series.SERVER_ERROR);
}

そして、hasErrorがtrueを返したら呼ばれるのが下記のメソッド。

4xx系の場合はHTTPClientErrorException、5xx系の場合はHTTPServerErrorExceptionが投げられる。

それ以外の場合はRestClientExceptionが投げられるみたいだけど、そもそも未知のステータスコードの場合は, hasError()の時点でgetHttpStatusCodeを呼んだ時点で例外が投げられるのでは?ここが呼ばれることはあるのか?ってなってるのが今。

@Override
public void handleError(ClientHttpResponse response) throws IOException {
    HttpStatus statusCode = getHttpStatusCode(response);
    switch (statusCode.series()) {
        case CLIENT_ERROR:
            throw new HttpClientErrorException(statusCode, response.getStatusText(),
                    response.getHeaders(), getResponseBody(response), getCharset(response));
        case SERVER_ERROR:
            throw new HttpServerErrorException(statusCode, response.getStatusText(),
                    response.getHeaders(), getResponseBody(response), getCharset(response));
        default:
            throw new RestClientException("Unknown status code [" + statusCode + "]");
    }
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした