Loading

ROUTE

ルートゼロの
アクティビティ

Spring Bootの@RequestParamと@PathVariableの違い|URLで判断

3

Spring Bootの`@RequestParam`と`@PathVariable`は、どちらもURLまわりの値をControllerで受け取るアノテーションですが、値を取り出す場所が違います。`/products/42`のようなパス内のIDは`@PathVariable`、`/products?category=book&page=2`のような検索条件は`@RequestParam`が基本です。URL設計で迷ったら「対象を特定する値か、一覧を絞り込む条件か」を先に考えましょう。

最初に比較:同じURLでも受け取る場所が違う

観点 @PathVariable @RequestParam
受け取る場所 URLパスの`{id}`などの変数 クエリ文字列(Spring MVCではフォームパラメータも対象)
代表URL `GET /products/42` `GET /products?category=book`
よくある用途 詳細対象のID、親子リソースの識別子 検索、並び替え、ページ番号、絞り込み
Controller側の例 `@PathVariable("id") Long id` `@RequestParam("category") String category`
省略可能な値 URLパターン自体との一致も考慮する `required=false`や`defaultValue`で扱える

どちらが技術的に優秀という話ではありません。APIのURLから何がリソースを指し、何が検索条件なのかが読み取れる形にするのが目的です。これらはSpring MVCのアノテーションであり、ここではServletベースのSpring Boot Web MVCを対象とします。


@PathVariable:URLパスのIDを受け取る

商品詳細を`/products/42`で表示する場合、`42`はパスの一部分です。`@GetMapping("/{id}")`と`@PathVariable`を組み合わせると、その部分を引数に受け取れます。

@RestController
@RequestMapping("/products")
class ProductController {
    private final ProductService products;

    ProductController(ProductService products) {
        this.products = products;
    }

    @GetMapping("/{id}")
    ProductDto findOne(@PathVariable("id") Long id) {
        return products.findById(id);
    }
}

`GET /products/42`なら`id`へ数値42が渡ります。上記の`ProductService`と`ProductDto`は、アプリケーション側で別途定義する型です。この例ではメソッドを短くして、受け取り方に絞っています。

パスに必要な部分が存在しなければ、まずURLがマッピングに一致するかどうかの問題になります。`@PathVariable(required=false)`を付けただけで`/products`へ自動的に同じメソッドが対応するわけではありません。省略を許すなら、別のマッピングやパターンを設計します。


@RequestParam:検索条件やページ番号を受け取る

一覧ページの「カテゴリ」と「何ページ目か」は、通常は特定リソースそのものではなく結果を調整する条件です。`?`以降のクエリパラメータに置くと、URLの意図が見えやすくなります。

@RestController
@RequestMapping("/products")
class ProductSearchController {
    private final ProductService products;

    ProductSearchController(ProductService products) {
        this.products = products;
    }

    @GetMapping
    List<ProductDto> search(
        @RequestParam(name = "category", required = false) String category,
        @RequestParam(name = "page", defaultValue = "0") int page
    ) {
        return products.search(category, page);
    }
}

`GET /products?category=book&page=2`なら`category`に`book`、`page`に2が入ります。`category`を省略した場合は`null`、`page`を省略した場合は既定値の0として扱います。Spring MVCではクエリだけでなく、フォームデータなどのServletリクエストパラメータも`@RequestParam`の対象です。

一覧APIでは不正な負数ページや過大なサイズが送られても安全に処理できるよう、値の検証と上限設定も実装してください。


URL設計は「対象」と「条件」を分けると決めやすい

やりたいこと URL例 推奨アノテーション
商品1件を取得 `/products/42` `@PathVariable`
商品一覧をカテゴリで検索 `/products?category=book` `@RequestParam`
商品一覧を並び替え `/products?sort=price` `@RequestParam`
ユーザーの注文を特定 `/users/7/orders/19` `@PathVariable`を複数
注文一覧を日付で絞る `/users/7/orders?from=2026-10-01` ユーザーIDは`@PathVariable`、期間は`@RequestParam`

たとえばユーザーIDを`?userId=7`で渡す設計も技術的に不可能ではありません。ここで示したのはAPI設計を分かりやすく保つための一般的な判断基準であって、ルール違反を機械的に判定するものではありません。

同じControllerで両方を組み合わせてもよい

実際のAPIでは、対象IDと表示条件を一緒に扱うケースがあります。

@GetMapping("/{id}/history")
List<HistoryDto> history(
    @PathVariable("id") Long id,
    @RequestParam(name = "limit", defaultValue = "20") int limit
) {
    return historyService.find(id, limit);
}

このメソッドなら`GET /products/42/history?limit=10`の`42`は商品ID、`10`は返す件数の条件です。上限チェックやアクセス権限の確認は別途必要です。URLのIDを受け取れたことは、そのリソースへのアクセス許可を得たことを意味しません。

値が取れないときの切り分け

  • `@PathVariable`が空になる → `@GetMapping`の`{id}`と`@PathVariable("id")`の名前が一致しているか確認する
  • `@RequestParam`を省略するとエラー → 既定で必須なので、必要なら`required=false`や`defaultValue`を指定する
  • 文字列から数値への変換に失敗 → `Long`や`int`の引数型とURLの入力値を確認する
  • URLが404になる → 引数以前に、HTTPメソッドとControllerのマッピングが一致しているか確認する
  • `@RequestBody`との違いが曖昧 → リクエスト本文のJSON受信とURL/フォームパラメータ受信は用途を分ける

欠落した必須クエリパラメータや数値変換失敗では、Spring MVCの標準構成で通常クライアントエラーとして扱われますが、実際のHTTP応答は例外ハンドラやアプリ設定によっても変わります。正確に確認するにはコントローラの結合テストで応答を見ましょう。

@RequestBodyや@ModelAttributeと混同しない

JSON本文をJavaオブジェクトへ読み込む`@RequestBody`と、クエリ・フォーム・パスを扱う今回の2つは用途が異なります。フォーム全体をまとめて受け取るなら`@ModelAttribute`も選択肢です。既存記事では、この2つの違いを詳しく整理しています。

(フォームオブジェクトの受け取り方を確認するについては『【初心者向け】Spring Boot のパラメータ受け取り|@ModelAttribute と @RequestBody の違いと使い分け』をご参照ください)

また、`@GetMapping`と`@PostMapping`はHTTPメソッドをどの処理へ結び付けるかという別の話です。

(GETとPOSTの違いを整理するについては『Spring Bootで理解するGETとPOSTの違い|Thymeleaf実装例付きで初心者の画面遷移・エラー対応まで丁寧解説』をご参照ください)

よくある質問

パスに検索キーワードを入れても動きますか?

URIパターンとしてマッピングできれば受け取れます。ただし検索語や絞り込み条件は変化・組合せが多いため、通常はクエリパラメータの方が扱いやすい設計になります。

@PathVariableと@RequestParamを1つのメソッドで使えますか?

はい。パス内の対象IDとクエリのフィルタ条件を同時に引数へ受け取れます。`/products/42/history?limit=10`などが一例です。

@RequestParamのrequired=falseとdefaultValueの違いは?

`required=false`は省略を許して、値がなければnullなどで受け取る使い方です。`defaultValue`は未指定や空値に代入する既定値を指定します。

@PathVariableのIDは安全な値ですか?

いいえ。URLの値は外部入力です。型変換だけに頼らず、必要な入力検証と認可を必ず行ってください。

まとめ:詳細IDはパス、一覧の絞り込みはクエリを第一候補に

  • `@PathVariable`はURLの`{id}`などを取り出し、特定対象の識別子に向く。
  • `@RequestParam`はクエリやフォームのリクエストパラメータを取り出し、検索条件やページ番号に向く。
  • 省略時の扱いは異なる。マッピングそのものと、引数が必須かどうかを分けて考える。
  • HTTPメソッド、入力検証、認可は別の設計要素として確認する。

既存のControllerで`@GetMapping`のURLパターンを確認し、各引数が「対象」なのか「条件」なのかを分類するところから始めてみてください。

カジュアル面談はこちら

RANKINGranking-icon

すべてを開示している会社です

ポジティブな面もネガティブな面も、包み隠さず誠実にお答えします。
気になることがあれば、面談の際にすべてオープンにお話します。