【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.java1 // バリデーションエラーがあればリダイレクト先にエラー情報を渡す 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.java1 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.html1 </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.java1 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.java1 // 投稿作成 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.java1 // 投稿更新 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.java1 // 投稿削除 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.java1 // バリデーションエラーがあればリダイレクト先にエラー情報を渡す 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", ...)でエラーメッセージをセットします。raはRedirectAttributesのインスタンスを指す変数名です。
コメントの保存に成功した場合は、ra.addFlashAttribute("successMessage", ...)で成功メッセージをセットします。どちらの処理の後も、投稿詳細ページへリダイレクトするため、フラッシュメッセージの仕組みが利用されています。
変更点: コメント削除処理へのフラッシュメッセージ追加
/src/main/java/com/example/bbs/controller/CommentController.java1 // コメント削除 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.html1 </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を使ってセットしたキー(successMessageやerrorMessage)と値が、リダイレクト先の画面で条件に応じて表示されるようになります。BootstrapのCSSクラス(alert, alert-successなど)を使うことで、メッセージが緑色や赤色の目立つボックスで表示されます。
おわりに
今回は、Spring Bootアプリケーションでフラッシュメッセージを実装する方法を学習しました。コントローラーでRedirectAttributesのaddFlashAttributeメソッドを使うことで、リダイレクト先に一度だけ有効なメッセージを渡せるようになります。そしてThymeleafの画面側では、th:ifでメッセージの有無を判断しth:textで内容を表示することで、投稿や削除といった操作の結果をユーザーに分かりやすく通知できます。