【Spring MVC】WebClientでGET通信(retriveでステータスハンドリング)

概要

retrieveメソッドを使用した、基本的なGET通信の使用方法についてまとめた。
今回はリクエスト送信後にエラー(4xx系や5xx系など)となった場合のハンドリング方法について紹介する。

尚、動作確認を行うための事前準備については、以下に記載している。

あわせて読みたい

概要 RESTなAPIにアクセスするのはRestTemplateではなくWebClientが今後推奨となるため、WebClientの概要についてまとめた。 WebClientを使用するための準備と、どんなメソッドがあるのかを紹介している[…]

WebClientの導入

 

基本的な使い方

getとretrieveメソッドを組み合わせることで、GETリクエストを送信する。
チェーンメソッドの一部としてretrieveの後にonStatusを組み合わせることで、HTTPステータスをもとにハンドリングを行うことができる。

 

実装方法

getメソッド/retriveメソッド/ResponseSpec#onStatusメソッドを、以下のようにメソッドチェーンで組み合わせて使用する。

 

webClient.get() // ⇒GETリクエストの準備
.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();
	}
}


 

 .onStatus(HttpStatus::is4xxClientError, res -> {

「HttpStatus::is4xxClientError」は、HttpStatusを受け取って4xx系であるかを条件判定する。
上記の条件がtrueの場合、res->{ … }の処理が実行される。

※ステータスハンドリングする数だけ、onStatusを定義する必要がある

 

res.bodyToMono(ApiErrorInfo.class)

ClientResponseが保持するレスポンスボディ情報を、APIエラー情報クラスにバインドしている。

 

body -> Mono.error(new ClientErrorException(body.getErrorTitle(), …)

「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を呼び出すことでラップした例外がスローされる

スポンサーリンク