概要
SpringのRestTemplateを使ったAPIコールにおいて、エラー時に投げられる例外クラスの種類をまとめた。
背景としては、SpringのRestTemplateを使っていて、これからエラー時の処理を実装するにあたって必要だと思ったので。
調査方法としては、RestTemplateのソースコードを漁ってみて、例外クラスのjavadocを見たり、実際にthrowされている箇所を見たりしてわかったことをまとめた。
環境
spring-web-4.3.16.RELEASE
例外クラスまとめ
RestTemplateの中には全部で7種類の例外が定義されていた。
それらをクラス図っぽくしてみたのが下記。
ひとつずつ見てみる。
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のレスポンスに関する例外を扱うクラス。
どうやら下記で述べるUnknownHTTPStatusCodeException
とHTTPStatusCodeException
に継承されているだけで、このクラスが名指しで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。
これは抽象クラスで、下記で述べるHTTPServerErrorException
とHTTPClientErrorException
に継承されている。
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 + "]");
}
}