Help us understand the problem. What is going on with this article?

簡単!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

yk0614
Webアプリケーションエンジニア
itpropartners
「自立」した人材を増やし、 "新しい仕事文化"をつくる」をテーマに、人材サービスを展開しているITプロパートナーズのエンジニアチームのOrganizationです。
https://itpropartners.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away