Spring FrameworkのRestClientに慣れていないので、勉強として使ってみる。
前提
- Spring Framework 6.2.5
- Kotlin 1.9.25
RestClientとは
RestClient はSpring Framework6.1から提供されている、HTTPリクエストを実行するためのクライアント。
RestClient (Spring Framework 6.2.12 API)
以前はRestTemplateを使うことが一般的だったが、Spring Framework 5.1からメンテナンスモードになっているため、今は同期的なリクエスト処理であればRestClient, 非同期的なリクエスト処理であればWebClientを使う形が良い。
RestTemplateとは異なりfluentAPIを採用しているため、WebClientと同様メソッドチェーンを使って処理を簡潔に書くことができる。
使い方
RestClientインスタンスの生成
RestClient用のBuilderが提供されているので、それを使ってインスタンスを生成する。
// デフォルトのRestClient val defaultClient = RestClient.create() // カスタマイズしたRestClient val customClient = RestClient.builder() .requestFactory(HttpComponentsClientHttpRequestFactory()) // HTTPライブラリを指定 .messageConverters { converters -> converters.add(MyCustomMessageConverter()) } // MessageConverter を追加 .baseUrl("https://example.com") // ベースURL .defaultUriVariables(mapOf("variable" to "foo")) // デフォルトのURI変数 .defaultHeader("My-Header", "Foo") // デフォルトのヘッダ .defaultCookie("My-Cookie", "Bar") // デフォルトのCookie .requestInterceptor(MyCustomInterceptor()) // RequestInterceptor を登録 .requestInitializer(MyCustomInitializer()) // RequestInitializer を登録 .build()
GETリクエスト(文字列で受け取る)
RestClientはfluentAPIが提供されているためメソッドをつなげて処理を書くことができる。
val result: String? = defaultClient.get() // GETリクエストの指定 .uri("https://example.com") // URIの指定 .retrieve() // レスポンスの取得 .body(String::class.java) // レスポンスボディをString型に変換
GETリクエスト(オブジェクトで受け取る)
data class Pet(var id: Int = 0, var name: String = "") val id = 123 val pet: Pet? = defaultClient.get() .uri("https://petclinic.example.com/pets/{id}", id) // URI変数をセット .accept(MediaType.APPLICATION_JSON) // Acceptヘッダをapplication/jsonに設定 .retrieve() .body(Pet::class.java) // JSONレスポンスをPetオブジェクトに変換
POSTリクエスト(リクエストボディなし)
val responseEntity: ResponseEntity<String> = defaultClient.post() .uri("https://example.com") .retrieve() .toEntity(String::class.java) // レスポンス全体をResponseEntityとして取得 val statusCode: HttpStatusCode = responseEntity.statusCode val headers: HttpHeaders = responseEntity.headers val body: String? = responseEntity.body
POSTリクエスト(リクエストボディあり)
// 送信する Pet オブジェクト (例) val newPet = Pet(name = "Buddy") val postResponse: ResponseEntity<Void> = defaultClient.post() // POSTリクエストの指定 .uri("https://petclinic.example.com/pets/new") .contentType(MediaType.APPLICATION_JSON) // Content-Typeヘッダをapplication/jsonに設定 .body(newPet) // リクエストボディを設定 .retrieve() .toBodilessEntity() // レスポンスボディなしの ResponseEntity を取得
エラーハンドリング
RestClientで4xx/5xxが返されるとデフォルトではRestClientExceptionという例外がthrowされる。
try { val errorResult: String? = defaultClient.get() .uri("https://example.com/this-url-does-not-exist") .retrieve() .body(String::class.java) } catch (e: RestClientException) { System.err.println("エラーが発生しました: ${e.message}") }
onStatus()というAPIで特定のエラーを対象に処理を書くことができる。
// 特定のステータスコードに対するハンドリング val resultWithStatusHandler: String? = defaultClient.get() .uri("https://example.com/this-url-does-not-exist") .retrieve() .onStatus({ statusCode -> statusCode.is4xxClientError }, { request, response -> throw RuntimeException("クライアントエラーが発生しました: ${response.statusCode}") }) .body(String::class.java)
リクエスト, レスポンスを使った柔軟な処理
exchange()というAPIを使うことで、リクエストとレスポンスを使ってより自由に処理を書くことができる。
val result: Pet? = defaultClient.get() .uri("https://petclinic.example.com/pets/{id}", id) .accept(MediaType.APPLICATION_JSON) .exchange { request, response -> if (response.statusCode.is4xxClientError) { // 4xx クライアントエラーのチェック throw RuntimeException("クライアントエラーが発生しました: ${response.statusCode}") } else { // レスポンスボディを Pet オブジェクトに変換 response.bodyTo(Pet::class.java) } }