概要
retrieveメソッドを使用した、基本的なGET通信の使用方法についてまとめた。
今回はリクエスト送信後にエラー(4xx系や5xx系など)となった場合のハンドリング方法について紹介する。
尚、動作確認を行うための事前準備については、以下に記載している。
概要 RESTなAPIにアクセスするのはRestTemplateではなくWebClientが今後推奨となるため、WebClientの概要についてまとめた。 WebClientを使用するための準備と、どんなメソッドがあるのかを紹介している[…]
基本的な使い方
getとretrieveメソッドを組み合わせることで、GETリクエストを送信する。
チェーンメソッドの一部としてretrieveの後にonStatusを組み合わせることで、HTTPステータスをもとにハンドリングを行うことができる。
実装方法
getメソッド/retriveメソッド/ResponseSpec#onStatusメソッドを、以下のようにメソッドチェーンで組み合わせて使用する。
.uri(URI情報) // ⇒URIを指定
.retrieve() // ⇒リクエストを送信し、レスポンスを取得
.onStatus( // ⇒ステータスハンドリングを行う数だけ追加
Predicate<HttpStatus>, // ⇒ ステータスを判定(例:4xx系や5xx系など)
Function<ClientResponse, Mono<? extends Throwable>> // ⇒ 条件一致時の処理(基本的にMono.errorで例外クラスをラップ)
)
.toEntity(取得したいレスポンスの型) // ⇒レスポンスをResponseEntityで取得
.block(); // ⇒Monoを同期的に処理し、中身を取り出す
このようにretrieveメソッドで送信した後、ハンドリングしたいステータス毎にResponseSpec#onStatusメソッドを呼び出してステータスハンドリングを行う。
ステータスの条件に一致した場合、レスポンス情報を保持するClientResponseを取得して処理を行う。
基本的にonStatusでハンドリングを行った場合、最終的に処理の結果としてMono.errorで例外クラスをラップして返却する。
そしてblockメソッドが呼ばれることで、ラップした例外がスローされる。
ClientResponse
onStatusで扱うClientResponseについて補足説明する。
ClientResponseはレスポンス全体(ステータスコード・ヘッダー・ボディなど)を扱うため、適宜必要な情報を参照して使用する。
※基本的にラップする例外に使用するため、ボディを取得することになると思われる
ClientResponseの補足
return webClient.get()
.uri(uri)
.retrieve()
.onStatus(HttpStatus::is4xxClientError, res -> {
// エラー時のレスポンス(ClientResponse)確認
System.out.println(res.statusCode() + "\n");
res.headers().asHttpHeaders().forEach((name, value) -> System.out.println(name + ": " + value));
// 4xx系ステータス
return res.bodyToMono(ApiErrorInfo.class)
.flatMap(body -> {
System.out.println("\n" + body);
return Mono.error(new ClientErrorException(body.getErrorTitle(),
body.getErrorMsg(), body.getStatus()));
});
})
.toEntity(responseType)
.block();
// エラー時のレスポンス(ClientResponse)確認
System.out.println(res.statusCode() + “\n”);
res.headers().asHttpHeaders().forEach((name, value) -> System.out.println(name + “: ” + value));
// 4xx系ステータス
return res.bodyToMono(ApiErrorInfo.class).flatMap(…)
このようにClientResponseオブジェクトを使用することで、HTTPレスポンスのステータスコード/ヘッダー/ボディを参照できる。
コンソール
400 BAD_REQUEST
Content-Type: [application/json;charset=UTF-8]
Transfer-Encoding: [chunked]
Date: [Sun, 08 Jun 2025 04:14:24 GMT]
Connection: [close]
ApiErrorInfo(status=400, errorTitle=入力エラー, errorMsg=入力に誤りあり, errorCode=SAMPLE_ERR0001, errors=[ApiErrorInfo.Item(target=name, msg=名前を入力してください)])
使用例
APIアクセスクラスにメソッドを追加して動作確認を行う。
APIアクセスクラス
get + retrieve + onStatusを使用して、ステータスハンドリング付きのGET通信を行うメソッドを追加する。
処理の流れとしては2xx系以外のステータスが返却された場合、例外をスローする。
WebApiClient.java
package com.example.webclient_prototype.biz;
import java.net.URI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import com.example.webclient_prototype.exception.ClientErrorException;
import com.example.webclient_prototype.exception.ServerErrorException;
import com.example.webclient_prototype.exception.UnknownErrorException;
import com.example.webclient_prototype.resource.error.ApiErrorInfo;
import reactor.core.publisher.Mono;
/**
* WebClientを使用したAPI疎通クラス
*/
@Component
public class WebApiClient {
@Autowired
private WebClient webClient;
/**
* GETリクエストを実行し、指定した型のボディを含むResponseEntityを取得する
* @param uri
* @param responseType
* @return
*/
public ResponseEntity getEntity(URI uri, Class responseType) {
return webClient.get()
.uri(uri)
.retrieve()
.onStatus(HttpStatus::is4xxClientError, res -> {
// 4xx系ステータス
return res.bodyToMono(ApiErrorInfo.class)
.flatMap(body -> Mono.error(new ClientErrorException(body.getErrorTitle(),
body.getErrorMsg(), body.getStatus())));
})
.onStatus(HttpStatus::is5xxServerError, res -> {
// 5xx系ステータス
return res.bodyToMono(ApiErrorInfo.class)
.flatMap(body -> Mono.error(new ServerErrorException(body.getErrorMsg())));
})
.onStatus(status -> !status.is2xxSuccessful(), res -> {
// 2xx, 4xx, 5xx以外の想定外ステータス
return Mono.error(new UnknownErrorException("想定外エラー"));
})
.toEntity(responseType)
.block();
}
}
「HttpStatus::is4xxClientError」は、HttpStatusを受け取って4xx系であるかを条件判定する。
上記の条件がtrueの場合、res->{ … }の処理が実行される。
※ステータスハンドリングする数だけ、onStatusを定義する必要がある
ClientResponseが保持するレスポンスボディ情報を、APIエラー情報クラスにバインドしている。
「Mono.error( … )」とすることで、例外オブジェクトを保持するMonoクラスを生成し、block()が呼ばれたタイミングでMonoが保持する例外がスローされる。
上記は、ClientResponseから必要な情報を取得して自作例外オブジェクトを生成している。
動作確認
動作確認を行う。
動作確認用クラス
try {
// GETリクエスト(400エラー発生)
client.getEntity(URI.create("http://localhost:8080/rest_prototype/rest06/?hogeDate=2025-02-01"), Resource.class);
} catch (ClientErrorException e) {
// 動作確認
System.out.println(e);
}
APIアクセスクラスに定義したメソッドにより、4xx系ステータスが返却された場合はClientErrorExceptionがスローされる。
コンソール
ClientErrorException(status=400, title=入力エラー, detailMessage=入力に誤りあり)
まとめ
☑ onStatusを使用することで、レスポンスステータスごとの振る舞いを定義できる
☑ Predicate<HttpStatus>によって、特定のHTTPステータスコードのマッチングが可能となる
☑ Mono.errorで例外をラップし、blockを呼び出すことでラップした例外がスローされる