
はじめに
はじめまして、2025年度新入社員の横田陽洋です。 4か月の研修を終え、8月から現場に配属されました。
現在Spring Bootを業務で扱っているのですが、ビギナー過ぎて日々詰まりまくっています。 今回はリダイレクト処理で詰まったときに色々調べて得た知識を共有したいと思います。
具体的には下記のような経緯となります。
ある一覧画面にて削除機能を追加したい
削除処理メソッドを実装
削除が失敗した場合、削除失敗メッセージを一覧画面に表示したい
削除の成否(boolean)を一覧画面のGETリクエスト処理にリダイレクトで渡したい
リダイレクト時のデータ受け渡しについての知識がなさ過ぎて悪戦苦闘
リダイレクト先にデータを渡す方法
結論を先にいうとredirectAttributes.addFlashAttributeを使えば、とりあえずOKです。
コードで書くと以下のようになります。
//リダイレクトする側 @PostMapping("/post") public String post(RedirectAttributes redirectAttributes) { redirectAttributes.addFlashAttribute("name", "yokota"); return "redirect:/get"; } //リダイレクト先 @GetMapping("/get") public String get(Model model) { return "/get"; }
redirectAttributesを使うことによって、フラッシュスコープでデータを渡すことができます。
また、addFlashAttributeを使うことによって、リダイレクト先のModelにデータが格納された状態にしてくれます。
わたしと同じようなビギナーの方たちは今の説明だけでは理解できないと思うので、かみ砕いて説明していきます。
なぜredirectAttributesを使うのか
データをビュー(画面)に渡すときは、以下のように書くと思います。
@GetMapping("/get") public String get(Model model) { //Modelに渡したいデータを格納 model.addAttribute("name", "yokota"); return "/get"; }
model.addAttributeを使えば、Model(ビューへ渡すための入れ物)にデータを格納してビューへ渡すことができます。
しかし、この方法ではリダイレクト先にデータを渡すことはできません。
なぜなら「スコープ」に問題があるからです。
スコープ
スコープとはデータが保持される期間などを表す概念です。
コントローラーからビューへデータを渡すときのスコープとしては以下の3つがあります。
| リクエストスコープ |
1回のリクエスト内でのみデータを保持。 次のリクエストでは消える。 |
|---|---|
| フラッシュスコープ |
リダイレクト先のリクエスト処理の終了までデータを保持。 1回だけリクエストを跨げる。 |
| セッションスコープ |
同じユーザがブラウザを閉じるまで、 またはセッションが切れるまでデータを保持。 複数リクエストを跨げる。 |
↓先ほどのmodel.addAttributeの例で出したコードが1回のリクエストだと思ってください。
//これが1回のリクエスト @GetMapping("/get") public String get(Model model) { //Modelに渡したいデータを格納 model.addAttribute("name", "yokota"); return "/get"; }
model.addAttributeの場合、データをModelに格納する際はリクエストスコープで格納しています。
スコープの表の通り、リクエストスコープは1回のリクエスト内でのみデータを保持します。
つまり、上記のようなリクエストの処理が終わるときにデータは消えてしまうので、リダイレクト先には渡せないのです。
ということで使うのが「redirectAttributes」です。
redirectAttributes
redirectAttributes.addFlashAttribute("name", name);
redirectAttributesはフラッシュスコープでModelにデータを格納してくれます。
フラッシュスコープはリクエストスコープとは違い、1回だけリクエストを跨ぐことができます。
つまり、リダイレクト先のリクエスト処理が終わるまでデータを保持してくれます。
addAttributeとaddFlashAttribute
冒頭でaddFlashAttributeを紹介しましたが、addFlashAttribute以外に「addAttribute」というのもあります。
String name = "yokota" redirectAttributes.addAttribute("name", name);
なぜこちらの方を紹介しなかったかというと、addAttributeだと「@RequestParam」または「@ModelAttribute」を使ってリダイレクト先でデータを受け取る必要があるからです。
...ということで、詳しく説明していきます。
addAttribute
redirectAttributes.addAttributeはURLのクエリパラメーターとして値を付け加えるだけです。
どういうことかというと、もともとのリダイレクト先のURLが以下のものであったとします。
http://localhost:8080/test
それが、redirectAttributes.addAttributeでデータ(name=yokota)を渡すと
http://localhost:8080/test?name=yokota
となります。
クエリパラメータとはURLの最後についている文字列です。上の例では「?name=yokota」のところです。
このようにURLのクエリパラメータに文字列が付け加えられるだけなので、リダイレクト先ではこれを読み取って受け取る処理が必要になります。
そこで使用するのが「@RequestParam」または「@ModelAttribute」です。
@RequestParam
以下のコードでは、addAttributeでリダイレクトした値を「@RequestParam」を使用して受け取っています。
//リダイレクトする側 @PostMapping("/post") public String post(RedirectAttributes redirectAttributes) { redirectAttributes.addAttribute("name", "yokota"); return "redirect:/get"; } //リダイレクト先 @GetMapping("/get") public String get(@RequestParam(value = "name") String name, Model model) { model.addAttribute("name", name); return "/get"; }
もう少しかみ砕いて説明すると
redirectAttributes.addAttribute("name", "yokota");
↑この記述では「yokota」という文字列を「name」という名前でマッピングして、リダイレクト先に送っています。
@RequestParam(value = "name") String name
↑この記述の中の「value = "name"」で「name」という名前でマッピングされた文字列「yokota」をString型の「name」に格納しています。 これによってリダイレクト先でも送られてきた文字列「yokota」を「name」という変数で扱えるわけです。
ちなみにですが、addAttributeは値を文字列としてしか渡すことはできませんが、@RequestParamで受け取るときの型を指定しておけばその型に自動変換して格納してくれます。(@ModelAttributeも同様)
redirectAttributes.addAttribute("flag", "true");
@RequestParam(value = "flag") boolean flag
上記のように書くと、きちんとboolean型の変数flagに「true」が格納されます。
@ModelAttribute
次は「@ModelAttribute」で受け取った場合です。
//リダイレクトする側 @PostMapping("/post") public String post(RedirectAttributes redirectAttributes) { redirectAttributes.addAttribute("name", "yokota"); return "redirect:/get"; } //リダイレクト先 @GetMapping("/get") public String get(@ModelAttribute("name") String name, Model model) { return "/get"; }
こちらのコードでも@RequestParamと同じように「name」という名前でマッピングされた文字列「yokota」を受け取っているのですが、リダイレクト先でModelに格納する処理がありません。
そうです。@ModelAttributeを使うと値の受け取りからModelへの格納までやってくれるのです。
と、データの受け取り方について説明してきましたが、実はaddFlashAttributeを使うとそんなことをしなくてもよいのです。
addFlashAttribute
addFlashAttributeを使用した場合を以下のコードに示します(冒頭のやつです)。
//リダイレクトする側 @PostMapping("/post") public String post(RedirectAttributes redirectAttributes) { redirectAttributes.addFlashAttribute("name", "yokota"); return "redirect:/get"; } //リダイレクト先 @GetMapping("/get") public String get(Model model) { return "/get"; }
見ての通り、リダイレクト先で@RequestParamと@ModelAttributeを使って値を受け取ったり、model.addAttributeでModelに格納したり、といった処理を書かなくてもいいのです。
挙動の違い
実装面での違いをこれまで説明してきましたが、最後に挙動面の違いについて説明したいと思います。
| URL | リロード時 | 用途 | |
|---|---|---|---|
| addAttribute | 値が表示される | 値が残る |
URLに値を残したい場合 (ブックマーク可能にしたいときなど) |
| addFlashAttribute | 表示されない | 値は残らない |
処理の後に一度だけ何かを表示したいとき (削除、登録完了メッセージなど) |
まずURLについてですが、上で説明していた通りaddAttributeはURLのクエリパラメータとして値を付け加えるので、リダイレクト先に渡した値はURL上に表示されます。
また、リロードしてもURLの状態は変わらないため、URLのクエリパラメータから値を受け取る処理を行う都合上、リロードしても引き続きその値を使用できます。
addFlashAttributeに関しては、表で示した通りURLに値が表示されることもなく、リロードするとその値は消失します。
結論
とりあえずリダイレクトするときは、「redirectAttributes.addFlashAttribute」を使えば何とかなる。
おわりに
新人あるあるだと思うのですが、わからないことを調べだすとその先でまたわからないことが見つかって、それを解決しようと調べるとまたわからないことが見つかる(以下無限ループ)
みたいなことになるので、「これはこういうもの」という感じで無理やり飲み込んで終わらせがちですよね。
このブログ執筆にあたりしっかり調べてみると、その無理やり飲み込んだ内容に結構誤りがあることがわかりました。
なので新人のうちは自分の知識を常に疑い、すぐに調べたほうがよさそうです。