素敵なサムシングを独断と偏見で一方的に紹介するブログ(´・ω・`)

投稿日: 2020年2月25日
最終更新日:

【bindFromRequestData】playFramework2.7で配列をマッピングする方法【BeanValidator】

YouTubeも見てね♪

ねこじゃすり

created by Rinker
PEPPY(ペピイ)
¥3,850 (2024/03/15 19:03:46時点 Amazon調べ-詳細)

猫を魅了する魔法の装備品!

Anker PowerCor

created by Rinker
Anker
¥4,990 (2024/03/15 15:06:44時点 Amazon調べ-詳細)

旅行には必須の大容量モバイルバッテリー!

[ノースフェイス] THE NORTH FACE メンズ アウター マウンテンライトジャケット

created by Rinker
THE NORTH FACE(ザノースフェイス)
¥29,280 (2024/03/15 19:06:01時点 Amazon調べ-詳細)

防水暴風で耐久性抜群なので旅行で大活躍です!

ドラゴンクエスト メタリックモンスターズギャラリー メタルキング

created by Rinker
スクウェア・エニックス(SQUARE ENIX)
¥3,250 (2024/03/15 19:03:48時点 Amazon調べ-詳細)

みんな大好き経験値の塊をデスクに常備しておこう!

Bauhutte ( バウヒュッテ ) 昇降式 L字デスク ブラック BHD-670H-BK

created by Rinker
Bauhutte(バウヒュッテ)
¥13,861 (2024/03/15 15:06:46時点 Amazon調べ-詳細)

メインデスクの横に置くのにぴったりなおしゃれな可動式ラック!

BANDAI SPIRITS ULTIMAGEAR 遊戯王 千年パズル 1/1スケール

created by Rinker
BANDAI SPIRITS(バンダイ スピリッツ)
¥7,180 (2024/03/15 15:06:46時点 Amazon調べ-詳細)

もう一人の僕を呼び覚ませ!!

MOFT X 【新型 ミニマム版】 iPhone対応 スマホスタンド

created by Rinker
MOFT
¥2,880 (2024/03/15 19:06:03時点 Amazon調べ-詳細)

Amazon一番人気のスマホスタンド!カード類も収納出来てかさ張らないのでオススメです!

配列クエリをFormモデルにバインドしようとしたら一癖あった話

PlayFrameworkを使ってAPI開発をしている際に、配列を想定しているクエリパラメーターに対してもBeanバリデーションを行いたかったため、Formモデルを使ってマッピングしようとしたところ、最初の要素しかListモデルにマッピング出来ませんでした。

今回はその原因と実際に試してみた対処方法についてご紹介しようと思います。

手順

前提

以下のようなidを複数指定して返却するようなAPIがあったとします。

まずはModelにマッピングせずに直接取得する方法

まずはController側で直接配列のクエリパラメーターを受け取ってみましょう。

以下のようなroutesの記述とControllerがあったとします。

確認

この場合idsの値は、http://localhost:9000/v1/accounts?ids=1&ids=4&ids=6の場合はids146http://localhost:9000/v1/accounts?ids=1の場合は1http://localhost:9000/v1/accountsの場合はidsempty(nullではない)となります。

しっかりマッピング出来てますね♪

Formモデルで取得

しかし、routesControllerの引数に直接追加すると、クエリパラメーターが増えたりした際の修正範囲が増えてしまいます。

PlayFrameworkのFormはPOSTなどのリクエストボディのマッピングで主に使うようですがGETのクエリパラメーターに対してもモデルにマッピングし、さらにBeanバリデーションを活用したいので試してみましょう。

以下の検索モデルを用意します。

今回はとりあえずなにかしらのBeanバリデーションをかけたいので、@Sizeを利用し要素が4件以上指定された場合にバリデーションエラーを発生させるようにしてみます。

routesControllerは以下のように修正します。

確認

では、実際にリクエストしてみましょう。

まず、http://localhost:9000/v1/accountsの場合はids=nullとなります。この場合はControllerの引数で直接取得する場合との差異が出てますね。

次にhttp://localhost:9000/v1/accounts?ids=1の場合。

こちらは正しくListの要素として4要素がマッピングされていますね。

最後に複数クエリパラメーターが指定されたhttp://localhost:9000/v1/accounts?ids=1&ids=4&ids=8&ids=10の場合です。

あら、一つ目の要素しかマッピングされていませんし、Beanバリデーターも機能していませんね。。。

試しにCriteriaモデルのidsの型をListから配列にしてもダメでした。orz

配列をマッピングしたい場合は[]を付けるのがplayのお作法

実は、PlayFrameworkでは配列パラメーターをマッピングしたい場合は[]を付けるのが基本となっているようです。

試しに、http://localhost:9000/v1/accounts?ids[]=1&ids[]=4&ids[]=8&ids[]=10でリクエストしてみましょう。

正しくids4要素マッピングされ、Beanバリデーションも実行されていますね!

とはいえ、[]を付けるのはちょっと気持ち悪い。。。

しかし、クエリパラメーターに[]を付けるのはちょっと一般的ではない気がするのと、呼び出し側も修正しないといけないのでちょっと採用したくない案ですよね。。。

そこで、クエリには[]をつけずにFormでバインドしてBeanバリデーションを実行する方法を以下のようにして実現してみました!(もっとスマートな方法があるかもしれません。orz)

配列を[]を使わずにFormモデルにbind&Beanバリデーションを実行する方法

routesの修正

まず、routesの設定として配列になりうる要素のみControllerの引数で取得出来るようにします。

この際に、Stringで定義しておくことがポイントです。

ControllerでFormにbindする部分を修正

次に、Controllerの引数で受け取った配列を利用して意図的にバインドする処理を追加します。

確認

では、http://localhost:9000/v1/accounts?ids=1&ids=4&ids=8&ids=10にアクセスして確認してみましょう。

これで一応期待通りの動きになりましたね!

終わりに

痒いところに手が届かない部分を自前でカスタマイズしてみました。

他にも良い対応策があると思いますが、同じようにモヤモヤしている方は参考にしてみていただければなと思います。

CATEGORIES & TAGS

IT