SpringのRestTemplateでAPIを呼び出す時にクエリパラメータをくっつける

RestTemplateでAPIを使うのにパラメータの設定方法を試しました。

はじめてSpring BootでRestTemplateを使ってAPIを呼び出すことになりました。

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations

HTTP リクエストを実行する同期クライアント。JDK HttpURLConnection、Apache HttpComponents などの基盤となる HTTP クライアントライブラリ上でシンプルなテンプレートメソッド API を公開します。 RestTemplate は、頻度の低いケースをサポートする一般化された exchange および execute メソッドに加えて、HTTP メソッドによる一般的なシナリオのテンプレートを提供します。

RestTemplate (Spring Framework 5.3.4 API) - Javadoc

クエリパラメータは、必須でない場合に設定したりしなかったりと可変になりますが、どんな方法がいいのでしょう? クエリパラメータを設定するにはどんな方法がいいのか試してみることにしました。

e-StatのAPIをサンプルに使います。

サンプルに呼び出すAPIは、政府統計の総合窓口(e-Stat)−API機能統計表情報取得を使います。

パラメータには以下の値を設定します。

パラメータ名 意味 設定する値
appId アプリケーションID e-Statのサイトで取得したアプリケーションID
openYears 公開年月 202102
statsField 統計分野 0204(人口移動)
limit データ取得件数 1

ControllerをでAPIを呼び出しちゃいます。

Serviceとかで呼ぶ方がいいと思いますが、とにかくパラメータをくっつける練習としてControllerでAPIを呼び出します。

package com.example.demo.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.JsonNode;

@RestController
@RequestMapping("/estat")
public class EstatController {
    private static final String URL = "http://api.e-stat.go.jp/rest/3.0/app/json";
    /** アプリケーションID. */
    private String appId = "02b...e-Statのサイトで取得したアプリケーションID";
    /** 公開年月. */
    private String openYears = "202102";
    /** 統計分野. */
    private String statsField = "0204";
    /** データ取得件数. */
    private String limit = "1";

    @GetMapping("/getStatsList")
    public JsonNode getStatsList() {
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<JsonNode> response = // .....ここに呼び出しの処理を書いていきます.........
        JsonNode jsonNode = response.getBody();
        return jsonNode;
    }
}

f:id:ponsuke_tarou:20210309210009j:plain
文京区の大黒湯

クエリパラメータをくっつける

  • 環境
    • macOS Big Sur バージョン11.1
    • Eclipse IDE for Enterprise Java Developers Version: 2020-12
    • spring-web 5.2.12.RELEASE
    • openjdk version "15.0.1"

パラメータを1つ1つ設定する

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException
指定された URL で GET を実行して、エンティティを取得します。レスポンスは変換され、ResponseEntity に保存されます。

パラメーター:
url - URL
responseType - 戻り値の型
uriVariables - テンプレートを展開する変数

上記のgetForEntityを使って書きました。

@GetMapping("/getStatsList")
public JsonNode getStatsList() {
    RestTemplate restTemplate = new RestTemplate();
    // 1. getForEntityでクエリパラメータ一つ一つを設定して、APIを呼び出す.
    ResponseEntity<JsonNode> response = restTemplate.getForEntity(
        URL + "/getStatsList?appId={appId}&openYears={openYears}&statsField={statsField}&limit={limit}",
        JsonNode.class,
        appId, openYears, statsField, limit);
    JsonNode jsonNode = response.getBody();
    return jsonNode;
}

UriComponentsBuilderでパラメータを設定する

public <T> ResponseEntity<T> exchange(RequestEntity<?> entity, Class<T> responseType) throws RestClientException
指定された RequestEntity で指定されたリクエストを実行し、レスポンスを ResponseEntity として返します。通常、たとえば RequestEntity の静的ビルダーメソッドと組み合わせて使用されます。

パラメーター:
entity - リクエストに書き込むエンティティ
responseType - 戻り値の型

UriComponentsBuilderでパラメータを設定して、上記のexchangeAPIを呼び出しました。

queryParamでパラメータ名と値を設定する

public UriComponentsBuilder queryParam(String name, Object... values)
指定されたクエリパラメーターを追加します。パラメーター名と値の両方に、後で値から展開される URI テンプレート変数を含めることができます。値が指定されていない場合、結果の URI にはクエリパラメーター名のみが含まれます。"?foo=bar" の代わりに "?foo"。

パラメーター:
name - クエリパラメーター名
values - クエリパラメーター値

UriComponentsBuilderのqueryParamを使ってクエリパラメータを設定しました。

@GetMapping("/getStatsList")
public JsonNode getStatsList() throws URISyntaxException {
    RestTemplate restTemplate = new RestTemplate();
    // 1. エンドポイントからUriComponentsBuilderを作成する.
    UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(URL + "/getStatsList");
    // 2. クエリパラメータを設定して文字列化する.
    String uri = builder.queryParam("appId", appId)
            .queryParam("openYears", openYears)
            .queryParam("statsField", statsField)
            .queryParam("limit", limit)
            .toUriString();
    // 3. パラメータを設定したURLからRequestEntityを作成する.
    RequestEntity<Void> requestEntity = RequestEntity.get(new URI(uri)).build();
    // 4. exchangeでAPIを呼び出す.
    ResponseEntity<JsonNode> response = restTemplate.exchange(requestEntity, JsonNode.class);
    JsonNode jsonNode = response.getBody();
    return jsonNode;
}

queryParamsでMultiValueMapを使って設定する

public UriComponentsBuilder queryParams(@Nullable MultiValueMap<String, String> params)
複数のクエリパラメーターと値を追加します。

パラメーター:
params - パラメーター

UriComponentsBuilderのqueryParamsを使ってクエリパラメータを設定しました。

1つのキーに複数の値を設定できるMap(org.springframework.util.MultiValueMap)

6.Spring3.0から追加された新しい部品の紹介 - soracane

MultiValueMapはよく使うMapとはちょっと違うもののようです。

@GetMapping("/getStatsList")
public JsonNode getStatsList() throws URISyntaxException {
    RestTemplate restTemplate = new RestTemplate();
    // 1. エンドポイントからUriComponentsBuilderを作成する.
    UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(URL + "/getStatsList");
    // 2. Mapにクエリパラメータを設定する.
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.add("appId", appId);
    params.add("openYears", openYears);
    params.add("statsField", statsField);
    params.add("limit", limit);
    // 3. Mapでクエリパラメータを設定して文字列化する.
    String uri = builder.queryParams(params).toUriString();
    // ...これ以降は「queryParamでパラメータ名と値を設定する」と同じ...

f:id:ponsuke_tarou:20210309210051j:plain
文京区の白山浴場