Rest Templateとは
Rest TemplateはREST APIを呼び出すためにSpring Frameworkが提供しているHTTPクライアントです。
Httpヘッダやボディ部を設定し、指定のURLにGETまたはPOSTで送信、返ってきた結果をjsonやobjectとして取得するなどが主な機能として挙げられます。
雑にまとめてしまえばcurl
コマンドをプログラムで叩くためのものです。
目次
・環境
・Beanの設定
・getForObject
・exchange
・応用編
・おまけ
・参考文献
環境
Kotlin:1.3.21
Spring Boot:2.1.5
Beanの設定
(※ご指摘のコメントをいただいたので追記。dais39さん、ありがとうございます。)
デフォルトのSpring BootではRestTemplateのBeanは自動で生成されないので、そのままだとDIコンテナから使用することができません。
なのでRestTemplateBuilder
を使用してBeanをDIコンテナに登録します。
@Configuration
class RestConfig{
@Bean
fun restTemplate(builder: RestTemplateBuilder): RestTemplate{
return builder.build()
}
}
今回は@Configuration
クラスを用いて定義しました。
getForObject
get送信をする方法にはgetForObject
メソッドを使う方法とexchange
メソッドを使う方法の二種類があります。
主にgetForObject
はリクエストヘッダを送る必要がない場合に使用されます。
今回はSpring Bootが提供しているhttp://gturnquist-quoters.cfapps.io/api/random
にアクセスしてjsonと自作のオブジェクトSampleObject
を取得してみます。
import ...
@RestController
// DIコンテナに登録されているRestTemplate()を注入します。
class SampleController(private val restTemplate: RestTemplate) {
// リクエスト送信先のURLを設定します。
private val uri: String = "http://gturnquist-quoters.cfapps.io/api/random"
// jsonファイルでのレスポンス取得
@GetMapping("/get/json")
fun getJson(): String? {
return restTemplate.getForObject(uri, String::class.java)
}
// objectでのレスポンス取得
@GetMapping("/get/object")
fun getObject(): String? {
val sampleObject = restTemplate.getForObject(uri, SampleObject::class.java)
return sampleObject.quote
}
}
import ...
data class SampleObject (
val type: String,
val value: Value
)
data class Value (
val id: Int,
val quote: String
)
これlocalhost:8080/get/jsonにアクセスするとjsonファイルが、localhost:8080/get/objectにアクセスするとランダムで表示されるquote
の値が表示されるようになりました。
もしクエリパラメータも送信したい場合はURLに文字列を合体させるか、exchangeの項目で説明するUriComponentsBuilder
を使用しましょう。
exchange
exchange
メソッドはGET、POSTどちらでも送信することができます。
主にリクエストヘッダを用いてBasic認証やOAuth認証をする必要がある際に使用されます。
import ...
@RestController
class SampleController(private val restTemplate: RestTemplate) {
private val uri: String = "http://gturnquist-quoters.cfapps.io/api/random"
@GetMapping("/get/exchange")
fun exchange(): ResponseEntity<String> {
return rt.exchange(url, HttpMethod.GET, null, String::class.java)
}
localhost:8080/get/exchangeにアクセスするとResponseEntity型の結果が返されます。(見た目はjsonとほぼ一緒)
応用編
以下のコードでは次のようなことを行なっています。
1.UriComponentsBuilderを使って送信先URIの設定(パスやクエリーの設定)
2.リクエストヘッダの設定(Basic認証の準備)
3.exchangeメソッドでResponseEntityを取得し、それのbody部分のみ(json)を取得
認証が必要ないい感じのAPIがなかったため、今回は適当な架空のURLを設定しました。
また処理を理解しやすくするためにかなり助長に書いています。
import ...
@RestController
class SampleController(private val restTemplate: RestTemplate) {
@GetMapping("/get/exchange/hoge")
fun getExchange(): String? {
// 1.リクエスト送信先のURLを設定します。
// リクエスト送信先のベースURLを設定します。
private val baseUri: String = "http://hogehoge.com"
// 送信先のパスとクエリーを設定します。
val getInstanceUrl = UriComponentsBuilder.fromHttpUrl(baseUri)
.path("/api/fuga")
.queryParam("comment", "test-text")
.build()
.toUri()
// これで getInstanceUrl = "http://hogehoge.com/api/fuga?comment=test-text" となります。
// 2.リクエストヘッダを設定します。
// Basic認証の設定
val userid: String = "1234567890"
val password: String = "pass_word"
// バイト列に変換してBase64でエンコード
val plainCredentials: String = userid + ":" + password
val base64Credentials: String = Base64.getEncoder()
.encodeToString(plainCredentials.getBytes(StandardCharsets.UTF_8))
// AuthorizationヘッダにBasic認証の資格情報を設定する。
val headers = HttpHeaders()
headers.set("Authorization", "Basic " + base64Credentials)
// HttpEntityにセット。
val entity: HttpEntity<String> = HttpEntity(headers)
// 3.やっとexchangeメソッドの出番
// 引数にURL, 送信メソッド, Entity, レスポンスデータの型をそれぞれ指定します。
// レスポンスはResponseEntity<指定した型>になります。
val responseEntity: ResponseEntity<String> = restTemplate.exchange(getInstancesURL, HttpMethod.GET, entity, String::class.java)
// bodyメソッドを使用することでbody部のjsonのみを出力します。
return responseEntity.body
}
}
複数のクエリパラメータを使用したい場合
複数のクエリを送信したい場合queryParam
メソッドを何度も使用するのは面倒です。
なのでqueryParams
メソッドを使用します。
このメソッドは引数がMultiValueMap<String, String>
なので注意しましょう。
val queryParams: Map<String, String> = mapOf("name" to "taro", "age" to "20")
val uriParams: MultiValueMap<String, String> = LinkedMultiValueMap()
queryParams.forEach { key, value -> uriParams.add(key, value) }
val getInstancesURL = UriComponentsBuilder.fromHttpUrl("http://hogehoge.com/api/fuga")
.queryParams(uriParams)
.build()
.toUri()
// getInstanceUrl = "http://hogehoge.com/api/fuga?name=taro&age=20"
OAuth認証を行いたい場合
今度はBasic認証ではなくOAuth認証に変更してみます。
val accessToken: String = "abc1234567890"
val headers = HttpHeaders()
headers.set("Authorization", "Bearer $accessToken")
// HttpEntityにセット。
val entity: HttpEntity<String> = HttpEntity(headers)
おまけ
オブジェクトとして取得する際にdata classで設定されていない値が帰ってきた場合の処理方法。
こんな感じでdata classを作った時に
import ...
data class SampleObject (
val type: String,
val value: Value
)
data class Value (
val id: Int,
val quote: String
)
こんな感じのjsonを取得すると
{
"type": "success",
"value": {
"id": 1,
"quote": "test dummy."
}
"created_at": 1476294126
}
こんな感じのエラーが出てきます。
Unrecognized field "created_at" (Class jp.example.SampleObject), not marked as ignorable
なのでそんな時は以下のようにして、未定義の値をスルーするようにしましょう。
@JsonIgnoreProperties(ignoreUnknown=true)
data class SampleObject (
...
)
参考文献
5.17. RESTクライアント(HTTPクライアント)
8.Springが用意するWEBクライアント(RestTemplate)の使い方
Spring BootでRestTemplateを使ってREST APIを呼び出す方法
SpringBoot:外部のサービスに接続
Get and Post Lists of Objects with RestTemplate
Spring MVC+RestTemplateで配列JSONをJavaBeanの配列又はListに変換する
かんたん!KotlinでJSONパース【Jackson】
Spring解体新書
Jackson の痒いところ Tips
Jackson使い方メモ
jacksonでデシリアライズする際に未知のプロパティを無視する
35. Calling REST Services with RestTemplate : Part IV. Spring Boot features