【Spring Boot】フラッシュメッセージを実装する|簡単な掲示板アプリの作成

Spring Bootで、掲示板アプリにフラッシュメッセージを実装する方法を解説します。投稿や削除の完了後にリダイレクトを挟むと、通常は画面にメッセージを表示できません。この問題を解決し、「投稿に成功しました」といった操作結果の通知を一度だけ表示する仕組みを、コントローラーとThymeleafを使って実装する具体的な手順を学びます。

作成日: 更新日:

開発環境

  • OS: Windows10
  • Visual Studio Code: 1.73.0
  • Java: OpenJDK 23
  • Spring Boot: 3.4.0
  • Lombok: 1.18.30
  • Thymeleaf Layout Dialect: 3.3.0
  • Spring Security: 6.1.1
  • Thymeleaf Extras Spring Security: 3.1.2.RELEASE
  • maven-compiler-plugin: 3.8.1
  • Spring Boot Starter Mail: 3.4.0
  • Spring Boot Starter Validation: 3.4.0

サンプルコード

/src/main/java/com/example/bbs/controller/CommentController.java

/src/main/java/com/example/bbs/controller/CommentController.java
1             // バリデーションエラーがあればリダイレクト先にエラー情報を渡す
2             ra.addFlashAttribute("org.springframework.validation.BindingResult.commentForm", result);
3             ra.addFlashAttribute("commentForm", commentForm);
4+
5+            // フラッシュメッセージをセット
6+            ra.addFlashAttribute("errorMessage", "コメントの投稿に失敗しました。");
7+
8             return "redirect:/posts/" + postId; // 元の投稿詳細画面にリダイレクト
9 
10         }
11         comment.setContent(commentForm.getContent());
12 
13         commentService.save(comment);
14+
15+        // フラッシュメッセージをセット
16+        ra.addFlashAttribute("successMessage", "コメントの投稿に成功しました。");
17+
18         return "redirect:/posts/" + postId;
19     }
20 
21     // コメント削除
22     @PostMapping("/{id}/delete")
23-    public String deleteComment(@PathVariable Long id, @RequestParam Long postId) {
24+    public String deleteComment(@PathVariable Long id, @RequestParam Long postId, RedirectAttributes ra) {
25         // 現在のログインユーザーを取得
26         User loggedInUser = userService.getCurrentUser();
27 
28 
29         // 投稿の所有者を確認
30         if (!commentService.verifyOwnership(comment, loggedInUser)) {
31+            // フラッシュメッセージをセット
32+            ra.addFlashAttribute("errorMessage", "コメントの削除に失敗しました。");
33+
34             // 所有者でない場合はエラーをスローまたはリダイレクト
35             return "redirect:/posts/" + postId + "?error=notAuthorized";
36         }
37 
38         // コメントを削除
39         commentService.deleteById(id);
40+
41+        // フラッシュメッセージをセット
42+        ra.addFlashAttribute("successMessage", "コメントの削除に成功しました。");
43+
44         return "redirect:/posts/" + postId;
45     }
46 }
47

/src/main/java/com/example/bbs/controller/PostController.java

/src/main/java/com/example/bbs/controller/PostController.java
1 import org.springframework.ui.Model;
2 import org.springframework.validation.BindingResult;
3 import org.springframework.web.bind.annotation.*;
4+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
5 
6 @Controller
7 @RequestMapping("/posts")
8 
9     // 投稿更新
10     @PostMapping("/{id}")
11-    public String updatePost(@PathVariable Long id, @ModelAttribute Post post) {
12+    public String updatePost(@PathVariable Long id, @ModelAttribute Post post, RedirectAttributes redirectAttributes) {
13         // 現在のログインユーザーを取得
14         User loggedInUser = userService.getCurrentUser();
15 
16 
17         // 投稿の所有者を確認
18         if (!postService.verifyOwnership(existingPost, loggedInUser)) {
19+            // フラッシュメッセージをセット
20+            redirectAttributes.addFlashAttribute("errorMessage", "掲示板の更新に失敗しました。");
21+
22             // 所有者でない場合はエラーをスローまたはリダイレクト
23             return "redirect:/posts?error=notAuthorized";
24         }
25         existingPost.setTitle(post.getTitle());
26         existingPost.setContent(post.getContent());
27         postService.save(existingPost);
28+
29+        // フラッシュメッセージをセット
30+        redirectAttributes.addFlashAttribute("successMessage", "掲示板の更新に成功しました。");
31+
32         return "redirect:/posts";
33     }
34 
35     // 投稿作成
36     @PostMapping
37-    public String createPost(@Valid @ModelAttribute("post") PostForm postForm, BindingResult result, Model model) {
38+    public String createPost(
39+            @Valid @ModelAttribute("post") PostForm postForm,
40+            BindingResult result,
41+            Model model,
42+            RedirectAttributes redirectAttributes) {
43         // バリデーションエラーがある場合、エラー情報を含めたフォーム画面へ戻す
44         if (result.hasErrors()) {
45+            // フラッシュメッセージをセット
46+            model.addAttribute("errorMessage", "掲示板の投稿に失敗しました。");
47+
48             model.addAttribute("post", postForm);
49             return "posts/new"; // 例:postForm.html というテンプレート名
50         }
51         post.setContent(postForm.getContent());
52         post.setUser(user);
53 
54+        // フラッシュメッセージをセット
55+        redirectAttributes.addFlashAttribute("successMessage", "掲示板の投稿に成功しました。");
56+
57         postService.save(post);
58         return "redirect:/posts";
59     }
60 
61     // 投稿削除
62     @PostMapping("{id}/delete")
63-    public String deletePost(@PathVariable Long id) {
64+    public String deletePost(@PathVariable Long id, RedirectAttributes redirectAttributes) {
65         // 現在のログインユーザーを取得
66         User loggedInUser = userService.getCurrentUser();
67 
68 
69         // 投稿の所有者を確認
70         if (!postService.verifyOwnership(post, loggedInUser)) {
71+            // フラッシュメッセージをセット
72+            redirectAttributes.addFlashAttribute("errorMessage", "掲示板の削除に失敗しました。");
73+
74             // 所有者でない場合はエラーをスローまたはリダイレクト
75             return "redirect:/posts?error=notAuthorized";
76         }
77 
78         // 投稿を削除
79         postService.deleteById(id);
80+
81+        // フラッシュメッセージをセット
82+        redirectAttributes.addFlashAttribute("successMessage", "掲示板の削除に成功しました。");
83+
84         return "redirect:/posts";
85     }
86 }
87

/src/main/resources/templates/layout/layout.html

/src/main/resources/templates/layout/layout.html
1             </div>
2           </nav>
3      </header>
4-    <!-- Main Content -->
5+    <!-- フラッシュメッセージ -->
6+    <div th:if="${successMessage}" class="alert alert-success alert-dismissible fade show" role="alert">
7+      <div class="text-center">
8+        <p th:text="${successMessage}"></p>
9+        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
10+      </div>
11+    </div>
12+
13+    <div th:if="${errorMessage}" class="alert alert-danger alert-dismissible fade show" role="alert">
14+      <div class="text-center">
15+        <p th:text="${errorMessage}"></p>
16+        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
17+      </div>
18+    </div>
19+
20+     <!-- Main Content -->
21     <main class="container mt-4" layout:fragment="content">
22 
23     </main>
24

コード解説

変更点: PostControllerへのRedirectAttributesのインポート

/src/main/java/com/example/bbs/controller/PostController.java
1 import org.springframework.ui.Model;
2 import org.springframework.validation.BindingResult;
3 import org.springframework.web.bind.annotation.*;
4+import org.springframework.web.servlet.mvc.support.RedirectAttributes;

フラッシュメッセージを実装するために、RedirectAttributesというクラスを利用します。この行は、そのクラスをPostController.javaファイルで使えるようにするために必要なインポート文です。RedirectAttributesは、リダイレクト(ページの自動転送)を行う際に、次のページに一度だけデータを渡すための特別な機能を提供します。

変更点: 投稿作成処理へのフラッシュメッセージ追加

/src/main/java/com/example/bbs/controller/PostController.java
1     // 投稿作成
2     @PostMapping
3-    public String createPost(@Valid @ModelAttribute("post") PostForm postForm, BindingResult result, Model model) {
4+    public String createPost(
5+            @Valid @ModelAttribute("post") PostForm postForm,
6+            BindingResult result,
7+            Model model,
8+            RedirectAttributes redirectAttributes) {
9         // バリデーションエラーがある場合、エラー情報を含めたフォーム画面へ戻す
10         if (result.hasErrors()) {
11+            // フラッシュメッセージをセット
12+            model.addAttribute("errorMessage", "掲示板の投稿に失敗しました。");
13+
14             model.addAttribute("post", postForm);
15             return "posts/new"; // 例:postForm.html というテンプレート名
16         }
17         post.setContent(postForm.getContent());
18         post.setUser(user);
19 
20+        // フラッシュメッセージをセット
21+        redirectAttributes.addFlashAttribute("successMessage", "掲示板の投稿に成功しました。");
22+
23         postService.save(post);
24         return "redirect:/posts";
25     }

投稿を作成するcreatePostメソッドに、フラッシュメッセージをセットする処理を追加しています。

まず、メソッドの引数にRedirectAttributes redirectAttributesを追加しました。これにより、Spring Bootが自動的にRedirectAttributesの機能を使えるように準備してくれます。

投稿内容のチェック(バリデーション)でエラーがあった場合は、リダイレクトせずに元の作成画面に戻ります。このときはmodel.addAttribute("errorMessage", ...)を使い、画面に「投稿に失敗しました」というメッセージを渡しています。

一方、投稿が成功した場合はreturn "redirect:/posts";で一覧ページにリダイレクトします。このリダイレクト先にメッセージを渡すためにredirectAttributes.addFlashAttribute("successMessage", ...)を使用します。addFlashAttributeでセットしたデータは、リダイレクト後のページで一度だけ表示された後、自動的に消えるという特徴があります。

変更点: 投稿更新処理へのフラッシュメッセージ追加

/src/main/java/com/example/bbs/controller/PostController.java
1     // 投稿更新
2     @PostMapping("/{id}")
3-    public String updatePost(@PathVariable Long id, @ModelAttribute Post post) {
4+    public String updatePost(@PathVariable Long id, @ModelAttribute Post post, RedirectAttributes redirectAttributes) {
5         // 現在のログインユーザーを取得
6         User loggedInUser = userService.getCurrentUser();
7 
8 
9         // 投稿の所有者を確認
10         if (!postService.verifyOwnership(existingPost, loggedInUser)) {
11+            // フラッシュメッセージをセット
12+            redirectAttributes.addFlashAttribute("errorMessage", "掲示板の更新に失敗しました。");
13+
14             // 所有者でない場合はエラーをスローまたはリダイレクト
15             return "redirect:/posts?error=notAuthorized";
16         }
17         existingPost.setTitle(post.getTitle());
18         existingPost.setContent(post.getContent());
19         postService.save(existingPost);
20+
21+        // フラッシュメッセージをセット
22+        redirectAttributes.addFlashAttribute("successMessage", "掲示板の更新に成功しました。");
23+
24         return "redirect:/posts";
25     }

投稿を更新するupdatePostメソッドにもフラッシュメッセージ機能を追加しています。

メソッドの引数にRedirectAttributes redirectAttributesを追加し、更新処理が成功した場合と失敗した場合の両方でメッセージをセットするように変更しました。

投稿の所有者でないユーザーが更新しようとした場合(失敗時)は、redirectAttributes.addFlashAttribute("errorMessage", ...)でエラーメッセージをセットして一覧ページにリダイレクトします。

更新処理が正常に完了した場合(成功時)は、redirectAttributes.addFlashAttribute("successMessage", ...)で成功メッセージをセットして一覧ページにリダイレクトします。どちらのケースでもリダイレクトを伴うため、addFlashAttributeが効果的に機能します。

変更点: 投稿削除処理へのフラッシュメッセージ追加

/src/main/java/com/example/bbs/controller/PostController.java
1     // 投稿削除
2     @PostMapping("{id}/delete")
3-    public String deletePost(@PathVariable Long id) {
4+    public String deletePost(@PathVariable Long id, RedirectAttributes redirectAttributes) {
5         // 現在のログインユーザーを取得
6         User loggedInUser = userService.getCurrentUser();
7 
8 
9         // 投稿の所有者を確認
10         if (!postService.verifyOwnership(post, loggedInUser)) {
11+            // フラッシュメッセージをセット
12+            redirectAttributes.addFlashAttribute("errorMessage", "掲示板の削除に失敗しました。");
13+
14             // 所有者でない場合はエラーをスローまたはリダイレクト
15             return "redirect:/posts?error=notAuthorized";
16         }
17 
18         // 投稿を削除
19         postService.deleteById(id);
20+
21+        // フラッシュメッセージをセット
22+        redirectAttributes.addFlashAttribute("successMessage", "掲示板の削除に成功しました。");
23+
24         return "redirect:/posts";
25     }

投稿を削除するdeletePostメソッドにも同様にフラッシュメッセージ機能を追加しています。

updatePostメソッドと同じように、引数にRedirectAttributes redirectAttributesを追加しました。そして、削除に失敗した場合(権限がないなど)にはエラーメッセージを、成功した場合には成功メッセージをredirectAttributes.addFlashAttributeでセットしてから、一覧ページへリダイレクトするように変更しています。

変更点: コメント投稿処理へのフラッシュメッセージ追加

/src/main/java/com/example/bbs/controller/CommentController.java
1             // バリデーションエラーがあればリダイレクト先にエラー情報を渡す
2             ra.addFlashAttribute("org.springframework.validation.BindingResult.commentForm", result);
3             ra.addFlashAttribute("commentForm", commentForm);
4+
5+            // フラッシュメッセージをセット
6+            ra.addFlashAttribute("errorMessage", "コメントの投稿に失敗しました。");
7+
8             return "redirect:/posts/" + postId; // 元の投稿詳細画面にリダイレクト
9 
10         }
11         comment.setContent(commentForm.getContent());
12 
13         commentService.save(comment);
14+
15+        // フラッシュメッセージをセット
16+        ra.addFlashAttribute("successMessage", "コメントの投稿に成功しました。");
17+
18         return "redirect:/posts/" + postId;
19     }

こちらはコメント投稿を処理するCommentControllerの変更点です。PostControllerと同様の仕組みを導入しています。

バリデーションエラーが発生した場合(失敗時)は、ra.addFlashAttribute("errorMessage", ...)でエラーメッセージをセットします。raRedirectAttributesのインスタンスを指す変数名です。

コメントの保存に成功した場合は、ra.addFlashAttribute("successMessage", ...)で成功メッセージをセットします。どちらの処理の後も、投稿詳細ページへリダイレクトするため、フラッシュメッセージの仕組みが利用されています。

変更点: コメント削除処理へのフラッシュメッセージ追加

/src/main/java/com/example/bbs/controller/CommentController.java
1     // コメント削除
2     @PostMapping("/{id}/delete")
3-    public String deleteComment(@PathVariable Long id, @RequestParam Long postId) {
4+    public String deleteComment(@PathVariable Long id, @RequestParam Long postId, RedirectAttributes ra) {
5         // 現在のログインユーザーを取得
6         User loggedInUser = userService.getCurrentUser();
7 
8 
9         // 投稿の所有者を確認
10         if (!commentService.verifyOwnership(comment, loggedInUser)) {
11+            // フラッシュメッセージをセット
12+            ra.addFlashAttribute("errorMessage", "コメントの削除に失敗しました。");
13+
14             // 所有者でない場合はエラーをスローまたはリダイレクト
15             return "redirect:/posts/" + postId + "?error=notAuthorized";
16         }
17 
18         // コメントを削除
19         commentService.deleteById(id);
20+
21+        // フラッシュメッセージをセット
22+        ra.addFlashAttribute("successMessage", "コメントの削除に成功しました。");
23+
24         return "redirect:/posts/" + postId;
25     }

コメントを削除するdeleteCommentメソッドの変更です。引数にRedirectAttributes raが追加されました。

コメントの所有者でないなど、削除に失敗した場合はra.addFlashAttributeを使ってエラーメッセージをセットします。削除に成功した場合は、同じくra.addFlashAttributeで成功メッセージをセットします。これにより、処理結果をリダイレクト先の画面でユーザーに通知できます。

変更点: 共通レイアウトへのフラッシュメッセージ表示領域の追加

/src/main/resources/templates/layout/layout.html
1      </header>
2-    <!-- Main Content -->
3+    <!-- フラッシュメッセージ -->
4+    <div th:if="${successMessage}" class="alert alert-success alert-dismissible fade show" role="alert">
5+      <div class="text-center">
6+        <p th:text="${successMessage}"></p>
7+        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
8+      </div>
9+    </div>
10+
11+    <div th:if="${errorMessage}" class="alert alert-danger alert-dismissible fade show" role="alert">
12+      <div class="text-center">
13+        <p th:text="${errorMessage}"></p>
14+        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
15+      </div>
16+    </div>
17+
18+     <!-- Main Content -->
19     <main class="container mt-4" layout:fragment="content">
20 
21     </main>

最後に、コントローラーでセットしたフラッシュメッセージを画面に表示するためのHTMLコードです。これは全ページで共通して読み込まれるレイアウトファイル(layout.html)に追加します。

th:if="${successMessage}"という記述は、Thymeleafというテンプレートエンジン(HTMLを動的に生成する技術)の機能です。これは、「もしsuccessMessageという名前のデータがコントローラーから渡されていたら、この<div>タグの中身を表示する」という意味になります。

同様に、th:if="${errorMessage}"errorMessageというデータが存在する場合に<div>タグを表示します。

<p th:text="${successMessage}"></p>は、successMessageのデータ(例:「投稿に成功しました」という文字列)を<p>タグのテキストとして表示する記述です。

これにより、コントローラーでaddFlashAttributeを使ってセットしたキー(successMessageerrorMessage)と値が、リダイレクト先の画面で条件に応じて表示されるようになります。BootstrapのCSSクラス(alert, alert-successなど)を使うことで、メッセージが緑色や赤色の目立つボックスで表示されます。

おわりに

今回は、Spring Bootアプリケーションでフラッシュメッセージを実装する方法を学習しました。コントローラーでRedirectAttributesaddFlashAttributeメソッドを使うことで、リダイレクト先に一度だけ有効なメッセージを渡せるようになります。そしてThymeleafの画面側では、th:ifでメッセージの有無を判断しth:textで内容を表示することで、投稿や削除といった操作の結果をユーザーに分かりやすく通知できます。

関連コンテンツ