何で受け取るべき?
Webのリクエストって基本的に文字列で飛んでくるじゃないですか?
で、それを受け取るFormクラスとか作ってControllerクラスのメソッドの引数に指定するじゃないですか?
プロジェクトによって...な、ところはあるけど、Formの中身を以下のいずれかで受け取ると思うんだよね。
- 型指定で受け取る(
Integer hogeId
とかLocalDate fugaDate
など - Stringで受け取る(
String hogeId
とかString fugaDate
など
結局どっちがいいの?
正直、アプリの作りによってな部分があると思われるけど、
どっちもメリット・デメリットは有ると思うので、好きな方を選べばいいよと思いつつ、
こういう作りだったらこっちじゃね?的な個人的な意見を書いてみようかなと。
とりあえず想定されるアプリの作りとして
- SpringBootを使ったWebアプリ(or 画面はないけどRestAPIとして公開)
- Controller←→Service間の値の受け渡しはForm or Form→Dto変換の2パターンのケース考慮
Controller←→Serviceの部分における実装のしやすさ的なことにフォーカスを当てて考えてる。
「型指定で受け取る」のメリ・デメ
メリット
- FormをそのままServiceに受け渡し出来る
→カプセル化やインターフェースとかの考え的にDTO変換してもいいけど、Formの中身と同じ定義の(Formの値を元に、別の値に変換するような処理がない)場合、変換する意味が無いと考えてしまうんよなぁ
→要は余計なリソースを作りたくないというめんどくさがりな考え - 余計な変換処理が無いため、後続処理の実装が楽になるかも
デメリット
- 型違いエラーの考慮が必要
→missMatchTypeだっけかのメッセージだったり考慮が必要 - 数値とかの桁数オーバーを考慮しないといけない
→Integer型の変数に画面でロクにチェックせず「9999999999」とか来たら死ぬやつ - オーバーフロー・存在しない日付・言語上処理出来ない日付が入ってるときの考慮をしないといけない
→@DateTimeFormat LocalDate hogeDate;
みたいに定義してれば2000/02/30とかでもなんかいい感じにしてくれるんじゃないかという期待はあるけど、そこをエラーで弾きたいってなった時には別途アノテーション作るしかない? - 実行時にControllerまでたどり着けない可能性があり、デバッグ確認が困難になるかも
→型違いでExceptionとか発生した場合、どこまでたどり着いているか追いかけるのが困難
利用にあたって
指定の型通り・最大桁数などを考慮したに値が来る(リクエスト送る側で事前にチェックしてる)という前提ならば、この作りでも良いのかなって気はします。
画面側とかの数値入力部分が<input type="text">
とかで、数字も文字など何でも入るようなクソみたいな作りだと、バックかフロントのどちらかが大きな負担になりそうな気はしますが。
「Stringで受け取る」のメリ・デメ
メリット
- そもそもリクエストが文字列なゆえに、何も考えずに値が設定される
→どうせチェック処理は書くんだから、とりあえず受け取っておける - 型のエラーは一旦回避できる
→なんでも受け止めてあげる精神 - 型変換については
ModelMapper
でForm→Dtoの時にやればいいので、カプセル化やインターフェースとかの考え的には良いのでは? - 数値のオーバーフローや日付の存在チェックなどを独自で設定できる
→設定しなきゃいけないと言う観点で言えばデメリットになる? - 実行時にとりあえずControllerまでたどり着くので、デバッグ実行でFormの中身が確認できる
- 扱いやすいString型に対してのチェック処理が書ける
デメリット
- 型チェック用の独自アノテーションの量産が必要
→1度作っちゃえば後のプロジェクトとかで使い回せるから最初だけ大変 -
ModelMapper
でも変換できない項目は、ゴリゴリConverterを実装しないといけない - カプセル化という言語思想上デメリットかと言われると微妙だけど、Form→Dto変換しないとイケない
利用にあたって
変換処理は必要だけど、何よりも「ちゃんと値が飛んできて、受け取れてるよね」の確認がエラー発生の少ない状態で進められるのは、初心者にとっては利点なのかなって気はする。
→ほらよくわからないExceptionとか出てControllerにすら辿り着けないと、どこが悪いのかすら追えなくて実装の土俵にすら立ててない感が凄いじゃん
個人的なまとめ
リクエスト元が誰なのかなども総合的にみて自分だったらこっちを選ぶみたいな目安。
選定する時にはバックエンドだけじゃなくフロントエンド側の作りとかも考慮して決めたほうが良いかもね。
リクエスト元が自分の管轄内(自サイト・自アプリ)の場合
- リクエスト元となるフロントの作りが丁寧な(ちゃんと型・サイズチェックなどしてる)場合は型指定Form
- リクエスト元となるフロントの作りが雑な場合はString型Form
リクエストを送ってくるフロントが明確にわかっている(SpringBootだとThymeleafとかで作られた画面とか自分が作って管理してる別アプリからリクエストが来る)場合は、そこの作りによって判断をしようかなと。
ある程度きちんとフロント側でチェックしてくれた上でリクエストをしてくれるんであれば、バック側は型の確認なんかせずに受け取ることが出来るんじゃないかなと。
逆にフロント側で特に制限なしに入れられた値をそのまま横流しするような作りだったら、変換エラーになってほしくないのでとりあえずStringで受け取っておくかって感じですね。
リクエスト元が自分の管轄外(APIとして外部公開してる、リクエスト元の作りが不明)の場合
missMatchTypeのメッセージとか、そういった例外処理関係をきちんと制御しているという前提であれば型指定Formでも良いかなとは思いますが、基本的には誰がリクエストしてくるかわからないようなシステムやAPIを作る場合はString型Form一択になるかなって感じですね。
誰がリクエストしてくるかわからない→いたずらに変なパラメータで飛ばしてくる可能性だって有る ということを懸念しておく必要があるのかなって。
フロントとか考慮しない、お前の本音は?
String型Form一択。。。
最初に型チェックのアノテーションを産む苦労をしてしまえば、今後の別案件とかで使い回せるメリットを考えたらむしろそれしか勝たんのではないかというね。
言っても、数値・日付さえ最初に抑えてしまえば後は出たとこ勝負になると思うので、何となく大丈夫な気はするんだよなぁ。。。
まぁ型チェックエラーの例外処理部分に対して共通処理作っちゃったほうが早くね?みたいな所もあるけど、共通で作った後に個別にナニカしようとすると影響がデカかったりするので、本当ならどっちにも対応できるような形を作っておけよって話なんだけどね。。。
また「画面側で絶対に制御するので、バックは信じて値を受け取れば良いです!」みたいなことを言う輩もいるかも知れませんが、そもそも単体テストとかで「画面以外から」リクエストを飛ばして動かすことも有るため、数値変換とかオーバーフローや日付問題とかを諸々と考えると、とりあえずStringで受け取ってなにかするほうが安全なんじゃないかなぁって気がする今日このごろでした。
まぁ。。。
仕様や設計がしっかりしてれば、実装なんて別におまけみたいなもんだから、好きな方でつくれや(身も蓋もない