きり丸アドベントカレンダー2020の23記事目です。
自システムのREST APIを作成するときには、@RestController
を付与すれば簡単に実装できます。
今回はRestOperationsを使用して、別システムのAPIを実行します。
ゴール
- アプリケーションから別システムに対してREST通信を行う
今回は、次のサイトが提供してくれている住所コードから住所名を取得するAPIを実行しましょう。トークン発行等が不要です。
APIの使い方
GETメソッドで次のURLを実行します。クエリパラメータで「zipcode」に住所コードを入力すると、住所名を取得できます。
https://zipcloud.ibsnet.co.jp/api/search?zipcode=2430018
レスポンスのBody:
{ "message": null, "results": [ { "address1": "神奈川県", "address2": "厚木市", "address3": "中町", "kana1": "カナガワケン", "kana2": "アツギシ", "kana3": "ナカチョウ", "prefcode": "14", "zipcode": "2430018" } ], "status": 200 }
環境
手順
外部連携用の設定ファイルを用意する
外部システムのURLを構築するための設定ファイルを用意します。現時点では分割する必要はありませんが、タイムアウト値等は共通的に持っておく方があとあと便利です。
YAMLファイルでの設定値を共通的にもつ方法は、こちらを参照してください。
ファイル名:application-external.yml
external: commons: &external-common schema: https connectionTimeout: 3 readTimeout: 3 zip-cloud: <<: *external-common host: zipcloud.ibsnet.co.jp path: /api/search
外部連携用の設定ファイルを読み込むJavaファイルを用意する
URLの作成ロジックは共通的に作っておいた方が楽です。ですので、私は設定ファイルを読み込むJava側でロジックを持ちます。
今回はUriComponentsBuilder
を使ってURLを構築します。
特殊な設定値はportくらいです。portを-1に設定した時にポート番号をURLに含めなくなります。
port | 生成されるURI |
---|---|
80 | http://localhost:80/api/v1 |
-1 | http://localhost/api/v1 |
また今回はクエリパラメータに?zipcode={値}
を設定したいので、クエリパラメータをURL生成時に作成します。
ファイル名:ExternalProperties.java
@Setter public abstract class ExternalProperties { private String schema; private String host; private int port; private String path; @Getter private int connectionTimeout; @Getter private int readTimeout; private UriComponentsBuilder createUriBuilder() { return UriComponentsBuilder.newInstance() .scheme(schema) .host(host) .port(port != 0 ? port : -1) .path(path); } public String getUrl(Map<String, String> map) { UriComponentsBuilder builder = createUriBuilder(); map.forEach(builder::queryParam); return builder.toUriString(); } }
実際に読み込ませるときは、作成したJavaファイルを継承しましょう。
@ConfigurationProperties
を指定することで、設定ファイルを読み込むことができます。
ファイル名:ZipCloudClientProperties.java
@Component @ConfigurationProperties(prefix = "external.zip-cloud") public class ZipCloudClientProperties extends ExternalProperties { }
APIの取得結果を受け取るDTOを作成する
レスポンスを受け取って処理をしたいので、型を作りましょう。
単純に項目名とマッピングできるDTOを作成すれば大丈夫です。
ファイル名:ZipCloudDto.java
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class ZipCloudDto { private String message; private List<ZipAddressDto> results; private String status; }
ファイル名:ZipAddressDto.java
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class ZipAddressDto { private String address1; private String address2; private String address3; private String kana1; private String kana2; private String kana3; private String prefcode; private String zipcode; }
APIを実行するコードを書く
RestTemplate
を使用して、別システムへアクセスします。
RestTemplateBuilder
からRestTemplate
を生成します。タイムアウト値等やインターセプターを設定してから生成できます。
今回使用する「ZipCloud」のAPIは、レスポンスのメディアタイプがtext/plain
なのでデフォルトだと受け取れません。text/plain
を受け取れるようにコンバータをRestTemplate
に追加します。
実際に他システムのAPIを実行するgetForEntity()
の第一パラメータにURL、第二パラメータに返却の型を指定します。
@Component @RequiredArgsConstructor public class ZipCloudClientImpl implements ZipCloudClient { private final ZipCloudClientProperties props; private final RestTemplateBuilder restTemplateBuilder; @Override public ZipCloudDto getAddressByZipcode(String zipCode) { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setSupportedMediaTypes(List.of(MediaType.TEXT_PLAIN)); RestTemplate restTemplate = restTemplateBuilder .setConnectTimeout(Duration.ofSeconds(props.getConnectionTimeout())) .setReadTimeout(Duration.ofSeconds(props.getReadTimeout())) .build(); restTemplate.getMessageConverters().add(converter); HashMap<String, String> hashMap = new HashMap<>(); hashMap.put("zipcode", zipCode); ResponseEntity<ZipCloudDto> responseEntity = restTemplate.getForEntity(props.getUrl(hashMap), ZipCloudDto.class); return responseEntity.getBody(); } }
テストコードを書く
RestTemplateBuilder
がSpringのDI機能を使用しているので、DIできるように@SpringJUnitConfig
を使用します。読み込む設定ファイルも必要になるので、RestTemplateAutoConfiguration.class
も指定します。
後は基本的にはコードを見ていただければ分かるかと思います。書いてあるのはセットアップとテストだけなので。なお、設定値を見ていただけると分かるとおり、相手の本番環境にアクセスします。
相手の環境がダウンしているとアクセス失敗し、テストが正常動作しません。挙動確認が終わり次第、自前でモックサーバーを立てるとよいでしょう。モックサーバーの建て方も記事にする予定です。
@SpringJUnitConfig(classes = { RestTemplateAutoConfiguration.class }) class ZipCloudClientImplTests { ZipCloudClient target; @Autowired private RestTemplateBuilder restTemplateBuilder; @BeforeEach void setup(){ ZipCloudClientProperties properties = new ZipCloudClientProperties(); properties.setSchema("https"); properties.setHost("zipcloud.ibsnet.co.jp"); properties.setPort(0); properties.setPath("/api/search"); target = new ZipCloudClientImpl(properties, restTemplateBuilder); } @Test void test_01(){ ZipCloudDto expected = ZipCloudDto.builder() .message(null) .results(List.of( ZipAddressDto.builder() .address1("神奈川県") .address2("厚木市") .address3("中町") .kana1("カナガワケン") .kana2("アツギシ") .kana3("ナカチョウ") .prefcode("14") .zipcode("2430018") .build() )) .status("200") .build(); ZipCloudDto actual = target.getAddressByZipcode("2430018"); assertThat(actual).isEqualTo(expected); } @Test void test_02(){ ZipCloudDto expected = ZipCloudDto.builder() .message("パラメータ「郵便番号」の桁数が不正です。") .status("400") .build(); ZipCloudDto actual = target.getAddressByZipcode("XXX"); assertThat(actual).isEqualTo(expected); } }
ソースコード
アドベントカレンダー23日目。
github.com
終わりに
自分で作ったシステムでできることには限界があります。やれることを増やすためにも、別システムを使用したり逆に情報を提供したりする必要があります。
REST通信できることで、やれることが大きく広がりますので、ぜひ試してみてください。
この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。
参考記事
敬称略
paiza開発日誌:面倒な手続き不要!「Web API」の超お手軽活用術をJavaScriptコード付きで一挙大公開! paiza.hatenablog.com
類似記事
きり丸アドベントカレンダー2020 adventar.org
きり丸のHerokuページ
https://kirimaru-todoapp.herokuapp.com/
24日目のアドベントカレンダーの記事 https://nainaistar.hatenablog.com/entry/2020/12/24/083000nainaistar.hatenablog.com