SpringでGETリクエストを複雑なオブジェクトにマッピングする。


やりたいこと(目的)

SpringのGETリクエスト(URLパラメータ)を複雑なオブジェクト(Bean)にマッピングする。

  • ネスト構造
  • リスト構造

結論

Mapping用のアノテーションを使用しない。
または
@ModelAttributeを使用する。

※変数名とURLパラメータのキーを合わせる必要あり。

環境

Spring Boot:2.1.6

その他使用ライブラリ

lombok

テキトーな検証ソース

マッピング先

親オブジェクト

@Data
@ToString
public class ComplexBean {
  private String hoge;
  private ChildBean childBean;
  private List<ItemBean> itemBeans;
}

ネスト用の子オブジェクト

@Data
@ToString
public class ChildBean {
  private String piyo;
}

リスト用のオブジェクト

@Data
@ToString
public class ItemBean {
  private String fuga;
}

@Data,@ToStringはlombokのアノテーション
@ToStringは検証用

コントローラ

@RestController
public class DemoRestController {

  @GetMapping(value = "/param")
  @ResponseBody
  public String getParam(@RequestParam ComplexBean bean) {
    System.out.println(bean.toString());
    return bean.toString();
  }

  @GetMapping(value = "/model")
  @ResponseBody
  public String getModel(@ModelAttribute ComplexBean bean) {
    System.out.println(bean.toString());
    return bean.toString();
  }

  @GetMapping(value = "/direct")
  @ResponseBody
  public String getDirect(ComplexBean bean) {
    System.out.println(bean.toString());
    return bean.toString();
  }
}

検証

Restlet Client - REST API Testingを用いて各エンドポイントにアクセス

検証するURLパラメータ

hoge=hoge&childBean.piyo=piyo&itemBeans[0].fuga=fuga0&itemBeans[2].fuga=fuga2

結果

@RequestParamを使用する場合
(http://localhost:8080/param?hoge=hoge&childBean.piyo=piyo&itemBeans[0].fuga=fuga0&itemBeans[2].fuga=fuga2)
マッピング情報が足りないのでパースできずエラーになる。

Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required ComplexBean parameter 'bean' is not present]

@ModelAttributeを使用する場合
(http://localhost:8080/model?hoge=hoge&childBean.piyo=piyo&itemBeans[0].fuga=fuga0&itemBeans[2].fuga=fuga2)
想定どおりマッピングされる。

アノテーションなしの場合
(http://localhost:8080/direct?hoge=hoge&childBean.piyo=piyo&itemBeans[0].fuga=fuga0&itemBeans[2].fuga=fuga2)
想定どおりマッピングされる。

まとめ

以下の構造を持つURLパラメータでも
- ネスト構造
- リスト構造

Mapping用のアノテーションを使用しない。
または
@ModelAttributeを使用する。

とすればマッピングできるがGETのリクエストでこんなオブジェクトにマッピングしなきゃいけなくなった時点で設計を見直そう
なお、検証のように配列番号を飛ばした場合は飛ばされた枝番はnullが設定される。(⇒空のオブジェクトにはならない)