Spring Bootの@RequestParamとFormクラスの違いを解説する記事のアイキャッチ。若手エンジニアがJava道場風の学習空間で入力値の受け取り方を学ぶイラスト。

実装・エラー解決

Spring Bootの@RequestParamとFormクラスの違いは?使い分けを解説

AIによる要約

@RequestParamは単発の値を受け取るときに便利です。一方、Formクラスは画面入力や検索条件のように複数項目を1つの入力として扱いたいときに向いています。迷ったら、値が業務上ひとまとまりかどうかで判断します。

新人SE
新人SE
@RequestParamで全部受け取れば、Formクラスっていらないんですか?
ポンコツSE
ポンコツSE
少ない検索条件ならそれでもよいですが、登録画面や項目が増える検索画面ではFormクラスにまとめた方が読みやすく、レビューでも説明しやすくなります。

Spring BootのControllerを書き始めると、@RequestParamで値を受け取る書き方と、Formクラスでまとめて受け取る書き方の両方が出てきます。

注文一覧にキーワードと状態を足す場合は @RequestParam で十分です。開始日と終了日が加わり、入力値とエラーを同じ検索画面に返すなら、GETでも @ModelAttribute でFormにまとめる理由があります。

この記事のポイント

  • @RequestParamは単発の値や簡単な検索条件に向く
  • Formクラスは複数項目を1つの入力として扱いたいときに向く
  • 登録・更新フォームではFormクラスを使うとバリデーションしやすい
  • 項目を一緒に検証する場合やエラー時に入力を残す場合はFormクラスを検討する

実行用サンプル

独立した2つの検索条件と、日付のエラーを画面に戻すFormを同じ注文データで比較します。 ソース全文、JUnitの検証コード、pom.xml、READMEを収録しています。JDK 17とMaven 3.9系で mvn test を実行します。Spring Boot 3.5.16のサンプルです。

@RequestParamは単発の値を受けるときに使いやすい

@RequestParamは、リクエストパラメータをControllerの引数で直接受け取るためのアノテーションです。検索キーワード、ページ番号、並び順など、単発で意味が分かる値に向いています。

OrderSearchController.java の抜粋。キーワードと状態だけを受け取るGET

    @GetMapping("/demo/orders/simple")
    @ResponseBody
    public List<Order> simple(@RequestParam(name = "keyword", required = false) String keyword,
                              @RequestParam(name = "status", required = false) String status) {
        return orderService.search(new OrderSearchCondition(keyword, status, null, null, null, 1));
    }

GET /demo/orders/simple?keyword=Coffee&status=CLOSED は注文3を返します。各項目は独立した絞り込み条件なので、2つの引数で受け取れば何を検索するか読めます。

Formクラスは入力項目をひとまとまりにする

一方で、入力項目が複数あり、それらを合わせて1つの検索条件や登録内容として扱うなら、Formクラスにまとめる方が自然です。

OrderSearchController.java の抜粋。6項目と入力エラーを受け取る検索GET

    @GetMapping("/orders")
    public String list(@Valid @ModelAttribute("searchForm") OrderSearchForm form,
                       BindingResult errors, Model model) {
        if (!errors.hasFieldErrors("fromDate") && !errors.hasFieldErrors("toDate")
                && form.getFromDate() != null && form.getToDate() != null
                && form.getFromDate().isAfter(form.getToDate())) {
            errors.rejectValue("toDate", "date.range", "終了日は開始日以降にしてください");
        }
        if (errors.hasErrors()) {
            return "orders/list";
        }
        model.addAttribute("orders", orderService.search(form.toCondition()));
        return "orders/list";
    }

GET /orders?keyword=Coffee&fromDate=bad では、Spring MVCが fromDate を LocalDate に変換できず、BindingResult にエラーを入れます。この例では検索を行わず、入力した Coffee と bad、日付のメッセージを検索画面に表示します。

登録画面ではFormクラスが基本になりやすい

登録や更新の画面では、名前、メールアドレス、電話番号、住所など、複数の値がセットで扱われます。この場合はFormクラスにまとめると、入力チェックも書きやすくなります。

登録では、たとえば名前とメールアドレスをフォームへまとめ、@NotBlank と @Email をフィールドに付けます。保存処理の前に BindingResult を見て、エラーなら入力画面を返します。このサンプルで実行するのは注文検索のGETです。

この形にしておくと、Controllerは「入力を受け取る」「エラーなら戻す」「問題なければServiceへ渡す」という役割に集中できます。

@RequestParamだけで頑張ると何が困るか

@RequestParamを大量に並べると、メソッドの引数が長くなり、何の画面入力なのか分かりにくくなります。レビューでは、まずここを見られます。

名前とメールアドレスをそれぞれ @RequestParam で受ける設計では、名前の空欄、メール形式、入力画面に戻すときの値をどこで扱うかも決めます。登録フォームにまとめると、入力の定義と検証をフィールド単位で見直せます。

判断が変わるのは引数が何個になったかより、入力を一緒に検証し、エラー時にそのまま表示する必要があるかです。2項目でも開始日と終了日なら、日付範囲としてFormを検討できます。

判断基準は、値が単発か、画面入力としてひとまとまりかです。単発なら@RequestParam、まとまりならFormクラスを検討します。

現場レビューではどう見られるか

レビューでは「日付の前後関係をどこで検証するか」「形式エラーのとき、入力した日付を画面に残せるか」まで確認します。

レビューで大事なのは、Formクラスを使っているかどうか自体ではありません。入力値のまとまりがコード上で表現されているか、変更しやすいか、入力チェックを置きやすいかです。

サンプルは、個別に受けた2項目で注文3を絞り込めることと、Formの型変換に失敗しても入力が画面に残ることをMockMvcで確かめています。両記事で使うサンプル全体の実行結果は以下です。

Tests run: 6, Failures: 0, Errors: 0, Skipped: 0

レビュー前のセルフチェック

確認リスト

  • キーワードと状態のように、個別の引数で意図が伝わるか
  • 登録・更新画面では入力値と項目ごとのエラーを同時に返せるか
  • 開始日と終了日のように、合わせて検証する条件があるか
  • Formクラスに画面入力以外の責務を持たせていないか

この記事とあわせて読みたい

関連記事Spring Bootの入力値はどこで受ける?@RequestParam・@PathVariable・@RequestBodyの使い分け Spring Bootの入力値は、URLの一部なら @PathVariable 、クエリ文字列なら @RequestParam 、JSONボディなら @RequestBody 、HTMLフォームならFormクラスで受けるのが基本です。値がどこから来るかを見ると迷いにくくなります。 記事を読む 関連記事BindingResultの使い方と注意点|@Validの直後に置く理由 BindingResult は、 @Valid で検出した入力エラーをControllerで受け取るための引数です。重要なのは、 @Valid を付けたFormの直後に置くことです。 記事を読む 関連記事Spring BootのFormとDTOの違い|Serviceへ渡すクラスを分ける判断 先に結論FormをServiceへ直接渡してよいかは、画面の入力契約とServiceの業務処理の契約が同じかで判断します。 記事を読む

まとめ

@RequestParamとFormクラスは、どちらが上位という関係ではありません。単発の値を受けるなら@RequestParam、複数項目を1つの入力として扱うならFormクラスが向いています。

新人のうちは、Controllerの引数が増えたときに一度立ち止まるだけでも十分です。入力値のまとまりを意識できると、Spring MVCのコードはかなり読みやすくなります。

-実装・エラー解決
-,