Java
WebAPI
spring
Thymeleaf
spring-boot

簡単!Spring BootでWebAPIをたたいてJSON形式のレスポンスをjavaオブジェクトに変換

More than 1 year has passed since last update.


概要

spring bootを使って世の中に公開されているwebAPIをたたいてみよう!

今回は一例として日本郵便が公開している『郵便番号検索API』を使用してみます。フォームで入力した郵便番号をリクエストURLにGETパラメータとして渡し、json形式のレスポンスを取得後、javaに変換しレスポンスの内容を画面に表示します。

開発環境


  • macOS Sierra 10.12

  • java version 1.8.0_65

  • Spring-Boot 1.3.5


    • Thymeleaf

    • Gradle



参考資料


完成図

入力フォーム

スクリーンショット 2016-11-19 15.20.16.png

送信結果

スクリーンショット 2016-11-19 15.21.43.png


Controller

/zipcodeにアクセスすると入力フォームの画面を表示します。

送信ボタンを押すと、/zipcode/confirmにアクセスされ、POSTパラメータとして渡されてきた郵便番号を@RequestParamで取得します。

取得してきた郵便番号を引数にAPIを呼んでいるサービスクラスを呼び出します。最後にAPIのレスポンスをmodelに詰めます。

ここではリストをmodelに詰めてthymeleafでリストの情報を表示していますが、controllerで一つずつmodelに詰めてもOK。


ZipCodeController.java

@Controller

public class ZipCodeController {

@Autowired
ZipCodeService zpcService;

/**
* 郵便番号入力フォーム
* @return "zipcode"
*/

@RequestMapping("/zipcode")
public String zipcodeForm(HttpSession session, Model model) {
return "zipcode";
}

/**
* 郵便番号情報表示
* @return "zipcode-confirm"
*/

@RequestMapping(value="/zipcode/confirm", method=RequestMethod.POST)
public String zipcodeConfirm(HttpSession session, Model model,
@RequestParam("zipcode") String zipcode){

// 一応必須チェックのみ 数字・桁数チェックは省略
// nullまたは空文字の場合、入力フォームにエラーメッセージを表示
if (zipcode == null || zipcode.equals("")) {
model.addAttribute("errorMessage", "郵便番号を入力してください。");
return zipcodeForm(session, model);
}

// 郵便番号検索APIサービス呼び出し
ZipCodeDto zipCodeDto = zpcService.service(zipcode);
// thymeleafでリストを展開して表示する
model.addAttribute("zipcodeList", zipCodeDto.getResults());
return "zipcode-confirm";
}
}



Service

Controllerから渡されてきた郵便番号をAPIのリクエストURLと結合させ、RestTemplateのpostForEntityメソッドの第一引数に渡しています。

RestTemplateとは?

RestTemplateは、REST API(Web API)を呼び出すためのメソッドを提供するクラスであり、 Spring Frameworkが提供するHTTPクライアントです。

※詳細はこちらを参照してください

response.getBody()でJSON文字列を取得します。

JSON文字列

スクリーンショット 2016-11-19 15.53.34.png

JSON文字列をパースするために、jsonパーサーライブラリのJacksonをインストールします。


build.gradle

dependencies {

compile("com.fasterxml.jackson.core:jackson-databind")
}

restTemplateのgetForObjectメソッドを第一引数にAPIのURL、第二引数に受け取りのDTO、第三引数にAPIパラメータを指定して呼び出します。


ZipCodeService.java


@Service
public class ZipCodeService {

@Autowired
@Qualifier("zipCodeSearchRestTemplate")
RestTemplate restTemplate;

/** 郵便番号検索API リクエストURL */
private static final String URL = "http://zipcloud.ibsnet.co.jp/api/search?zipcode={zipcode}";

public ZipCodeDto service(String zipcode) {
return restTemplate.getForObject(URL, ZipCodeDto.class, zipcode);
}

}



RestTemplateResolver

追記

コメントに以下の指摘頂き、RestTemplateResolverクラスを作成します。

RestTemplateは内部でHttpMessageConverterを使ってHTTPのBODYとJavaオブジェクトの変換を行っているのですが、Jackson用のHttpMessageConverterの実装クラス(MappingJackson2HttpMessageConverter)は、content-typeがapplication/jsonとかじゃないとダメなんですよね・・・。


RestTemplateResolver.java

@Component

public class RestTemplateResolver {

@Bean
public RestTemplate zipCodeSearchRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>(messageConverter.getSupportedMediaTypes());
supportedMediaTypes.add(MediaType.TEXT_PLAIN); // text/plainのJacksonの処理対象にくわえる
messageConverter.setSupportedMediaTypes(supportedMediaTypes);
restTemplate.setMessageConverters(Collections.singletonList(messageConverter)); // カスタムしたHttpMessageConverterを適用
return restTemplate;
}
}



Dto


ZipCodeDto.java

    /** ステータス */

int status;

/** メッセージ */
String message;

/** 郵便番号情報リスト */
List<ZipCodeDataDto> results = new ArrayList<>();



ZipCodeDataDto.java

   /** 郵便番号 */

String zipcode;

/** 都道府県コード */
String prefcode;

/** 都道府県名 */
String address1;

/** 市区町村名 */
String address2;

/** 町域名 */
String address3;

/** 都道府県名カナ */
String kana1;

/** 市区町村名カナ */
String kana2;

/** 町域名カナ */
String kana3;


※setter、getterは省略


Template

テンプレートエンジンはthymeleafを使用。

リストの中の要素を一つずつ取り出したい場合はth:each="変数 : ${コレクション}"で記述できる。th:objectを定義しておくことで、*{変数}でオブジェクトの中の要素を取得できる。他のthymeleafの記法についてはここでは割愛する。


zipcode-confirm.html

th:each="item : ${zipcodeList}" th:object="${item}"

th:text="*{zipcode}"
th:text="*{prefcode}"
th:text="*{address1}"

・(省略)

th:text="*{kana3}"


使用したコード

https://github.com/keiyonekawa0614/awakenoy0614/tree/develop/Sample/src/main/java/com/example


WebAPI使ってみたいけど、どうしたらいいかわからない。JSONをJavaオブジェクトにどうやってパースするの?APIってどうやって呼ぶの?っていう人、私も少し前までそうでした。駆け出しエンジニアです。情報不足なとこが多々あるかもしれませんが、この記事を読んで、何か役に立つことがあれば幸いです。