【Spring Boot】お問い合わせフォームを作成する|簡単な掲示板アプリの作成

この記事では、Spring BootとThymeleafを使い、お問い合わせフォームをゼロから作成する方法を解説します。フォームの入力、確認、送信完了といった一連の流れに加え、Spring Securityでのアクセス設定や、メール送信機能の実装方法、`application.properties`での詳細な設定まで学べます。実用的なWebアプリ開発の基礎を習得しましょう。

作成日: 更新日:

開発環境

  • 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

サンプルコード

/pom.xml

/pom.xml
1 			<artifactId>thymeleaf-extras-springsecurity6</artifactId>
2 			<version>3.1.2.RELEASE</version>
3 		</dependency>
4+		<dependency>
5+		    <groupId>org.springframework.boot</groupId>
6+		    <artifactId>spring-boot-starter-mail</artifactId>
7+			</dependency>
8 	</dependencies>
9 
10 	<build>
11

/src/main/java/com/example/bbs/config/SecurityConfig.java

/src/main/java/com/example/bbs/config/SecurityConfig.java
1     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
2         http
3                 .authorizeHttpRequests(auth -> auth
4-                        .requestMatchers("/", "/auth/register", "/auth/login").permitAll()
5+                        .requestMatchers("/", "/auth/register", "/auth/login", "/contact/**").permitAll()
6                         .anyRequest().authenticated())
7                 .formLogin(form -> form
8                         .loginPage("/auth/login")
9

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

/src/main/java/com/example/bbs/controller/ContactController.java
1+package com.example.bbs.controller;
2+
3+import org.springframework.stereotype.Controller;
4+import org.springframework.ui.Model;
5+import org.springframework.web.bind.annotation.GetMapping;
6+import org.springframework.web.bind.annotation.PostMapping;
7+import org.springframework.web.bind.annotation.RequestMapping;
8+import org.springframework.web.bind.annotation.RequestParam;
9+import org.springframework.web.bind.annotation.SessionAttributes;
10+import com.example.bbs.model.ContactData;
11+
12+import com.example.bbs.service.EmailService;
13+
14+@Controller
15+@RequestMapping("/contact")
16+@SessionAttributes("contactData")
17+public class ContactController {
18+
19+    private EmailService emailService;
20+
21+    public ContactController(EmailService emailService) {
22+        this.emailService = emailService;
23+    }
24+
25+    @GetMapping
26+    public String contactForm(Model model) {
27+        // セッションに値があれば表示
28+        if (!model.containsAttribute("contactData")) {
29+            model.addAttribute("contactData", new ContactData());
30+        }
31+        return "contact/form";
32+    }
33+
34+    @PostMapping("/confirm")
35+    public String confirmContact(
36+            @RequestParam("name") String name,
37+            @RequestParam("email") String email,
38+            @RequestParam("message") String message,
39+            Model model) {
40+        model.addAttribute("contactData", new ContactData(name, email, message));
41+        return "contact/confirm";
42+    }
43+
44+    @GetMapping("/complete")
45+    public String completeForm() {
46+        return "contact/complete";
47+    }
48+
49+    @PostMapping("/submit")
50+    public String submitContact(
51+            @RequestParam("name") String name,
52+            @RequestParam("email") String email,
53+            @RequestParam("message") String message) {
54+
55+        // ユーザーに送信するメール内容
56+        String userSubject = "お問い合わせありがとうございます";
57+        String userBody = String.format(
58+                "%s様\n\nお問い合わせありがとうございます。\n以下の内容で受け付けました。\n\n[お問い合わせ内容]\n%s",
59+                name, message);
60+
61+        // 運営者に送信するメール内容
62+        String adminSubject = "新しいお問い合わせが届きました";
63+        String adminBody = String.format(
64+                "新しいお問い合わせが届きました。\n\n[お名前]: %s\n[メールアドレス]: %s\n[お問い合わせ内容]:\n%s",
65+                name, email, message);
66+
67+        // ユーザー宛てにメール送信
68+        emailService.sendUserEmail(email, userSubject, userBody);
69+
70+        // 運営者宛てにメール送信
71+        emailService.sendAdminEmail(adminSubject, adminBody);
72+
73+        return "redirect:/contact/complete";
74+    }
75+}
76

/src/main/java/com/example/bbs/model/ContactData.java

/src/main/java/com/example/bbs/model/ContactData.java
1+package com.example.bbs.model;
2+
3+import lombok.Getter;
4+import lombok.Setter;
5+
6+@Getter
7+@Setter
8+public class ContactData {
9+    private String name;
10+    private String email;
11+    private String message;
12+
13+    public ContactData() {
14+    }
15+
16+    public ContactData(String name, String email, String message) {
17+        this.name = name;
18+        this.email = email;
19+        this.message = message;
20+    }
21+}
22

/src/main/java/com/example/bbs/service/EmailService.java

/src/main/java/com/example/bbs/service/EmailService.java
1+package com.example.bbs.service;
2+
3+import org.springframework.beans.factory.annotation.Value;
4+import org.springframework.mail.SimpleMailMessage;
5+import org.springframework.mail.javamail.JavaMailSender;
6+import org.springframework.stereotype.Service;
7+
8+@Service
9+public class EmailService {
10+
11+    private JavaMailSender mailSender;
12+
13+    @Value("${mail.from}") // application.properties から読み込み
14+    private String fromAddress;
15+
16+    @Value("${mail.admin}") // application.properties から読み込み
17+    private String adminAddress;
18+
19+    public EmailService(JavaMailSender mailSender) {
20+        this.mailSender = mailSender;
21+    }
22+
23+    public void sendUserEmail(String to, String subject, String text) {
24+        sendEmail(to, subject, text);
25+    }
26+
27+    public void sendAdminEmail(String subject, String text) {
28+        sendEmail(adminAddress, subject, text);
29+    }
30+
31+    public void sendEmail(String to, String subject, String text) {
32+        SimpleMailMessage message = new SimpleMailMessage();
33+        message.setTo(to);
34+        message.setSubject(subject);
35+        message.setText(text);
36+        message.setFrom(fromAddress);
37+        mailSender.send(message);
38+    }
39+}
40

/src/main/resources/application.properties

/src/main/resources/application.properties
1 
2 # Thymeleafログを有効化
3 spring.thymeleaf.cache=false
4-logging.level.org.thymeleaf=DEBUG
5+logging.level.org.thymeleaf=DEBUG
6+
7+# mailの設定
8+spring.mail.host=smtp.gmail.com
9+spring.mail.port=587
10+spring.mail.username=engr.sng@gmail.com
11+spring.mail.password=vjostbnxevshsajl
12+spring.mail.properties.mail.smtp.auth=true
13+spring.mail.properties.mail.smtp.starttls.enable=true
14+
15+# カスタム設定 (任意)
16+mail.from=engr.sng@gmail.com
17+mail.admin=engr.sng@gmail.com
18

/src/main/resources/templates/contact/complete.html

/src/main/resources/templates/contact/complete.html
1+<!DOCTYPE html>
2+<html layout:decorate="layout/layout">
3+<head>
4+    <title layout:fragment="title">お問い合わせ送信完了</title>
5+</head>
6+
7+<main layout:fragment="content">
8+    <div class="container mt-5 text-center">
9+        <h1 class="mb-4">お問い合わせありがとうございました!</h1>
10+        <p>お問い合わせ内容は正常に送信されました。</p>
11+        <a href="/" class="btn btn-primary">トップページに戻る</a>
12+    </div>
13+</main>
14

/src/main/resources/templates/contact/confirm.html

/src/main/resources/templates/contact/confirm.html
1+<!DOCTYPE html>
2+<html layout:decorate="layout/layout">
3+<head>
4+    <title layout:fragment="title">お問い合わせ確認画面</title>
5+</head>
6+
7+<main layout:fragment="content">
8+    <h1 class="mb-4">お問い合わせ確認画面</h1>
9+    <form th:action="@{/contact/submit}" method="post">
10+        <div class="mb-3">
11+            <label class="form-label">お名前</label>
12+            <p th:text="${contactData.name}" class="form-control-plaintext"></p>
13+            <input type="hidden" name="name" th:value="${contactData.name}">
14+        </div>
15+        <div class="mb-3">
16+            <label class="form-label">メールアドレス</label>
17+            <p th:text="${contactData.email}" class="form-control-plaintext"></p>
18+            <input type="hidden" name="email" th:value="${contactData.email}">
19+        </div>
20+        <div class="mb-3">
21+            <label class="form-label">お問い合わせ内容</label>
22+            <p th:text="${contactData.message}" class="form-control-plaintext"></p>
23+            <input type="hidden" name="message" th:value="${contactData.message}">
24+        </div>
25+        <button type="submit" class="btn btn-primary">送信する</button>
26+        <a href="/contact" class="btn btn-secondary">戻る</a>
27+    </form>
28+</main>
29

/src/main/resources/templates/contact/form.html

/src/main/resources/templates/contact/form.html
1+<!DOCTYPE html>
2+<html layout:decorate="layout/layout">
3+<head>
4+    <title layout:fragment="title">お問い合わせフォーム</title>
5+</head>
6+
7+<main layout:fragment="content">
8+    <div class="container mt-5">
9+        <h1 class="mb-4">お問い合わせフォーム</h1>
10+        <form th:action="@{/contact/confirm}" method="post">
11+            <div class="mb-3">
12+                <label for="name" class="form-label">お名前</label>
13+                <input type="text" id="name" name="name" class="form-control" placeholder="例: 山田 太郎" th:value="${contactData.name}" required>
14+            </div>
15+            <div class="mb-3">
16+                <label for="email" class="form-label">メールアドレス</label>
17+                <input type="email" id="email" name="email" class="form-control" placeholder="例: example@gmail.com" th:value="${contactData.email}" required>
18+            </div>
19+            <div class="mb-3">
20+                <label for="message" class="form-label">お問い合わせ内容</label>
21+                <textarea id="message" name="message" class="form-control" rows="5" placeholder="お問い合わせ内容をご記入ください" th:text="${contactData.message}" required></textarea>
22+            </div>
23+            <button type="submit" class="btn btn-primary">確認画面へ進む</button>
24+        </form>
25+    </div>
26+</main>
27

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

/src/main/resources/templates/layout/layout.html
1                       <button type="submit" class="nav-link active">Logout</button>
2                     </form>
3                   </li>
4+                  <li class="nav-item">
5+                    <a class="nav-link" href="/contact">お問い合わせ</a>
6+                  </li>
7                 </ul>
8               </div>
9             </div>
10

コード解説

変更点: メール送信機能の依存関係を追加しました

/pom.xml
1 			<artifactId>thymeleaf-extras-springsecurity6</artifactId>
2 			<version>3.1.2.RELEASE</version>
3 		</dependency>
4+		<dependency>
5+		    <groupId>org.springframework.boot</groupId>
6+		    <artifactId>spring-boot-starter-mail</artifactId>
7+			</dependency>
8 	</dependencies>
9 
10 	<build>
11

この変更では、pom.xmlというファイルに新しい「依存関係(dependency)」を追加しました。pom.xmlは、あなたのプロジェクトが動作するために必要な外部ライブラリ(部品)を管理するファイルです。ここで追加したのは、spring-boot-starter-mailというライブラリで、これはSpring Bootアプリケーションでメールを送信するための機能を提供します。このライブラリを追加することで、プログラムからメールを送る準備が整います。

変更点: お問い合わせフォームのURLアクセス権限を設定しました

/src/main/java/com/example/bbs/config/SecurityConfig.java
1     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
2         http
3                 .authorizeHttpRequests(auth -> auth
4-                        .requestMatchers("/", "/auth/register", "/auth/login").permitAll()
5+                        .requestMatchers("/", "/auth/register", "/auth/login", "/contact/**").permitAll()
6                         .anyRequest().authenticated())
7                 .formLogin(form -> form
8                         .loginPage("/auth/login")
9

この変更は、Spring Securityというセキュリティ設定を行うファイルです。アプリケーションのどのURLに誰がアクセスできるかを定義しています。 以前は、トップページやログイン・登録ページだけがログインなしでアクセス可能でした。今回の変更では、これに加えて/contact/**、つまり/contactで始まるすべてのURL(お問い合わせフォーム、確認画面、完了画面など)にpermitAll()を設定しました。これにより、ユーザーがログインしていなくても、お問い合わせフォームを利用できるようになりました。

変更点: お問い合わせフォームのデータモデルを作成しました

/src/main/java/com/example/bbs/model/ContactData.java
1+package com.example.bbs.model;
2+
3+import lombok.Getter;
4+import lombok.Setter;
5+
6+@Getter
7+@Setter
8+public class ContactData {
9+    private String name;
10+    private String email;
11+    private String message;
12+
13+    public ContactData() {
14+    }
15+
16+    public ContactData(String name, String email, String message) {
17+        this.name = name;
18+        this.email = email;
19+        this.message = message;
20+    }
21+}
22

この変更では、ContactData.javaという新しいファイルを作成しました。これは「モデル」と呼ばれるクラスで、お問い合わせフォームから送られてくる「お名前」「メールアドレス」「お問い合わせ内容」といったデータをまとめて管理するための設計図です。 @Getter@Setterは、Lombokというライブラリの機能で、このクラスの各情報(name, email, message)に対して、値を取得するgetメソッドと、値を設定するsetメソッドを自動的に作ってくれます。 また、引数なしの空のコンストラクタと、全ての情報を受け取って初期化するコンストラクタが定義されています。これにより、フォームから受け取ったデータを扱いやすくなります。

変更点: メール送信サービスを作成しました

/src/main/java/com/example/bbs/service/EmailService.java
1+package com.example.bbs.service;
2+
3+import org.springframework.beans.factory.annotation.Value;
4+import org.springframework.mail.SimpleMailMessage;
5+import org.springframework.mail.javamail.JavaMailSender;
6+import org.springframework.stereotype.Service;
7+
8+@Service
9+public class EmailService {
10+
11+    private JavaMailSender mailSender;
12+
13+    @Value("${mail.from}") // application.properties から読み込み
14+    private String fromAddress;
15+
16+    @Value("${mail.admin}") // application.properties から読み込み
17+    private String adminAddress;
18+
19+    public EmailService(JavaMailSender mailSender) {
20+        this.mailSender = mailSender;
21+    }
22+
23+    public void sendUserEmail(String to, String subject, String text) {
24+        sendEmail(to, subject, text);
25+    }
26+
27+    public void sendAdminEmail(String subject, String text) {
28+        sendEmail(adminAddress, subject, text);
29+    }
30+
31+    public void sendEmail(String to, String subject, String text) {
32+        SimpleMailMessage message = new SimpleMailMessage();
33+        message.setTo(to);
34+        message.setSubject(subject);
35+        message.setText(text);
36+        message.setFrom(fromAddress);
37+        mailSender.send(message);
38+    }
39+}
40

この変更では、EmailService.javaという新しいファイルを作成しました。これは「サービス」と呼ばれるクラスで、メール送信という特定の機能(ビジネスロジック)を担当します。 @Serviceアノテーションは、このクラスがSpringによって管理されるサービスであることを示します。 JavaMailSenderは、Springが提供するメール送信を行うための部品です。コンストラクタでこの部品を受け取ることで、メール送信機能を使えるようにしています。 @Valueアノテーションは、application.propertiesという設定ファイルから、送信元のメールアドレス(mail.from)と管理者(運営者)のメールアドレス(mail.admin)を読み込むために使われます。 sendUserEmailメソッドはユーザー(お問い合わせをしてくれた人)にメールを送るためのもので、sendAdminEmailメソッドは管理者(ウェブサイトの運営者)にメールを送るためのものです。 sendEmailメソッドは、実際にメールを作成して送信する共通の処理を担っています。SimpleMailMessageオブジェクトを作成し、送信先、件名、本文、送信元を設定し、mailSender.send()を呼び出すことでメールが送信されます。

変更点: お問い合わせフォームのコントローラーを作成しました

/src/main/java/com/example/bbs/controller/ContactController.java
1+package com.example.bbs.controller;
2+
3+import org.springframework.stereotype.Controller;
4+import org.springframework.ui.Model;
5+import org.springframework.web.bind.annotation.GetMapping;
6+import org.springframework.web.bind.annotation.PostMapping;
7+import org.springframework.web.bind.annotation.RequestMapping;
8+import org.springframework.web.bind.annotation.RequestParam;
9+import org.springframework.web.bind.annotation.SessionAttributes;
10+import com.example.bbs.model.ContactData;
11+
12+import com.example.bbs.service.EmailService;
13+
14+@Controller
15+@RequestMapping("/contact")
16+@SessionAttributes("contactData")
17+public class ContactController {
18+
19+    private EmailService emailService;
20+
21+    public ContactController(EmailService emailService) {
22+        this.emailService = emailService;
23+    }
24+
25+    @GetMapping
26+    public String contactForm(Model model) {
27+        // セッションに値があれば表示
28+        if (!model.containsAttribute("contactData")) {
29+            model.addAttribute("contactData", new ContactData());
30+        }
31+        return "contact/form";
32+    }
33+
34+    @PostMapping("/confirm")
35+    public String confirmContact(
36+            @RequestParam("name") String name,
37+            @RequestParam("email") String email,
38+            @RequestParam("message") String message,
39+            Model model) {
40+        model.addAttribute("contactData", new ContactData(name, email, message));
41+        return "contact/confirm";
42+    }
43+
44+    @GetMapping("/complete")
45+    public String completeForm() {
46+        return "contact/complete";
47+    }
48+
49+    @PostMapping("/submit")
50+    public String submitContact(
51+            @RequestParam("name") String name,
52+            @RequestParam("email") String email,
53+            @RequestParam("message") String message) {
54+
55+        // ユーザーに送信するメール内容
56+        String userSubject = "お問い合わせありがとうございます";
57+        String userBody = String.format(
58+                "%s様\n\nお問い合わせありがとうございます。\n以下の内容で受け付けました。\n\n[お問い合わせ内容]\n%s",
59+                name, message);
60+
61+        // 運営者に送信するメール内容
62+        String adminSubject = "新しいお問い合わせが届きました";
63+        String adminBody = String.format(
64+                "新しいお問い合わせが届きました。\n\n[お名前]: %s\n[メールアドレス]: %s\n[お問い合わせ内容]:\n%s",
65+                name, email, message);
66+
67+        // ユーザー宛てにメール送信
68+        emailService.sendUserEmail(email, userSubject, userBody);
69+
70+        // 運営者宛てにメール送信
71+        emailService.sendAdminEmail(adminSubject, adminBody);
72+
73+        return "redirect:/contact/complete";
74+    }
75+}
76

この変更では、ContactController.javaという新しいファイルを作成しました。これは「コントローラー」と呼ばれるクラスで、ユーザーからのWebリクエストを受け取り、適切な処理を行い、結果をユーザーに返す役割を担います。 @Controllerアノテーションは、このクラスがコントローラーであることを示します。 @RequestMapping("/contact")は、/contactで始まるURLへのリクエストがこのコントローラーで処理されることを意味します。 @SessionAttributes("contactData")は、contactDataという名前のデータがセッション(ユーザーごとに一時的にデータを保存する場所)に保存されることを示します。これにより、フォームの入力内容を確認画面や完了画面に引き継ぐことができます。

  • public ContactController(EmailService emailService): EmailService(先ほど作成したメール送信機能)をこのコントローラーで使えるように受け取っています。
  • @GetMappingメソッド(contactForm):/contactへのGETリクエスト(ページを初めて開くときなど)を処理し、お問い合わせ入力フォームの画面(contact/form.html)を表示します。もしセッションにcontactDataがなければ、新しいContactDataオブジェクトを準備します。
  • @PostMapping("/confirm")メソッド(confirmContact):入力フォームから送られてきたデータ(POSTリクエスト)を受け取り、確認画面(contact/confirm.html)を表示します。@RequestParamは、フォームの入力項目から値を受け取るために使われます。受け取った値はContactDataオブジェクトに格納し、画面に渡します。
  • @GetMapping("/complete")メソッド(completeForm):お問い合わせ送信完了画面(contact/complete.html)を表示します。
  • @PostMapping("/submit")メソッド(submitContact):確認画面から「送信する」ボタンが押されたとき(POSTリクエスト)に処理されます。
    • ここでも@RequestParamを使って、確認画面から送られてきたお名前、メールアドレス、お問い合わせ内容を受け取ります。
    • ユーザー宛てと運営者宛てのメールの件名と本文を作成しています。
    • emailService.sendUserEmail()emailService.sendAdminEmail()を呼び出して、実際にメールを送信します。
    • 最後にreturn "redirect:/contact/complete";とすることで、メール送信後にユーザーを送信完了画面へ自動的に移動させます。

変更点: メール送信設定を application.properties に追加しました

/src/main/resources/application.properties
1 
2 # Thymeleafログを有効化
3 spring.thymeleaf.cache=false
4-logging.level.org.thymeleaf=DEBUG
5+logging.level.org.thymeleaf=DEBUG
6+
7+# mailの設定
8+spring.mail.host=smtp.gmail.com
9+spring.mail.port=587
10+spring.mail.username=engr.sng@gmail.com
11+spring.mail.password=vjostbnxevshsajl
12+spring.mail.properties.mail.smtp.auth=true
13+spring.mail.properties.mail.smtp.starttls.enable=true
14+
15+# カスタム設定 (任意)
16+mail.from=engr.sng@gmail.com
17+mail.admin=engr.sng@gmail.com
18

この変更では、application.propertiesというファイルにメール送信に関する設定を追加しました。application.propertiesは、データベース接続情報やログレベル、今回のメール設定など、アプリケーション全体の動作に関わる設定を記述する場所です。

  • spring.mail.host:メールを送信するために使うSMTPサーバーのホスト名を指定します。ここではGmailのSMTPサーバーを使用しています。
  • spring.mail.port:SMTPサーバーのポート番号を指定します。
  • spring.mail.username:SMTPサーバーにログインするためのユーザー名(メールアドレス)です。
  • spring.mail.password:SMTPサーバーにログインするためのパスワードです。
  • spring.mail.properties.mail.smtp.auth=true:SMTP認証を有効にすることを示します。
  • spring.mail.properties.mail.smtp.starttls.enable=true:メールの通信を暗号化するためにSTARTTLSを使用することを有効にしています。
  • mail.from:送信元として表示されるメールアドレスを指定します。
  • mail.admin:管理者にメールを送る際の、その管理者メールアドレスを指定します。 これらの設定により、アプリケーションがどのメールサーバーを使って、どのような認証情報でメールを送るかが定義されました。

変更点: お問い合わせ入力フォームのHTMLテンプレートを作成しました

/src/main/resources/templates/contact/form.html
1+<!DOCTYPE html>
2+<html layout:decorate="layout/layout">
3+<head>
4+    <title layout:fragment="title">お問い合わせフォーム</title>
5+</head>
6+
7+<main layout:fragment="content">
8+    <div class="container mt-5">
9+        <h1 class="mb-4">お問い合わせフォーム</h1>
10+        <form th:action="@{/contact/confirm}" method="post">
11+            <div class="mb-3">
12+                <label for="name" class="form-label">お名前</label>
13+                <input type="text" id="name" name="name" class="form-control" placeholder="例: 山田 太郎" th:value="${contactData.name}" required>
14+            </div>
15+            <div class="mb-3">
16+                <label for="email" class="form-label">メールアドレス</label>
17+                <input type="email" id="email" name="email" class="form-control" placeholder="例: example@gmail.com" th:value="${contactData.email}" required>
18+            </div>
19+            <div class="mb-3">
20+                <label for="message" class="form-label">お問い合わせ内容</label>
21+                <textarea id="message" name="message" class="form-control" rows="5" placeholder="お問い合わせ内容をご記入ください" th:text="${contactData.message}" required></textarea>
22+            </div>
23+            <button type="submit" class="btn btn-primary">確認画面へ進む</button>
24+        </form>
25+    </div>
26+</main>
27

この変更では、contact/form.htmlという新しいHTMLテンプレートファイルを作成しました。これは、ユーザーがお問い合わせ内容を入力するための画面です。 layout:decorate="layout/layout"は、layout/layout.htmlという共通のレイアウトファイルを使って、ヘッダーやフッターなどの共通部分を自動的に含めることを意味します。 このファイルには、お名前、メールアドレス、お問い合わせ内容を入力するための入力欄(<input>タグと<textarea>タグ)が配置されています。 th:action="@{/contact/confirm}"は、フォームが送信されたときに、ContactControllerconfirmContactメソッド(URLが/contact/confirmのPOSTリクエスト)にデータが送られることを示します。 th:value="${contactData.name}"th:text="${contactData.message}"は、Thymeleafというテンプレートエンジンを使って、Javaのオブジェクト(この場合はContactData)から値を取得し、入力欄の初期値として表示することを意味します。これは、例えば確認画面から「戻る」ボタンを押して入力画面に戻った際に、以前入力した内容が残っているようにするために使われます。

変更点: お問い合わせ確認画面のHTMLテンプレートを作成しました

/src/main/resources/templates/contact/confirm.html
1+<!DOCTYPE html>
2+<html layout:decorate="layout/layout">
3+<head>
4+    <title layout:fragment="title">お問い合わせ確認画面</title>
5+</head>
6+
7+<main layout:fragment="content">
8+    <h1 class="mb-4">お問い合わせ確認画面</h1>
9+    <form th:action="@{/contact/submit}" method="post">
10+        <div class="mb-3">
11+            <label class="form-label">お名前</label>
12+            <p th:text="${contactData.name}" class="form-control-plaintext"></p>
13+            <input type="hidden" name="name" th:value="${contactData.name}">
14+        </div>
15+        <div class="mb-3">
16+            <label class="form-label">メールアドレス</label>
17+            <p th:text="${contactData.email}" class="form-control-plaintext"></p>
18+            <input type="hidden" name="email" th:value="${contactData.email}">
19+        </div>
20+        <div class="mb-3">
21+            <label class="form-label">お問い合わせ内容</label>
22+            <p th:text="${contactData.message}" class="form-control-plaintext"></p>
23+            <input type="hidden" name="message" th:value="${contactData.message}">
24+        </div>
25+        <button type="submit" class="btn btn-primary">送信する</button>
26+        <a href="/contact" class="btn btn-secondary">戻る</a>
27+    </form>
28+</main>
29

この変更では、contact/confirm.htmlという新しいHTMLテンプレートファイルを作成しました。これは、ユーザーが入力したお問い合わせ内容を確認するための画面です。 ここでもlayout:decorate="layout/layout"を使って共通レイアウトを利用しています。 画面には、入力フォームで入力されたお名前、メールアドレス、お問い合わせ内容が、編集できない形で表示されます。th:text="${contactData.name}"のように、Thymeleafを使ってContactDataオブジェクトから値を取り出し、画面に表示しています。 重要なのは、<input type="hidden" name="name" th:value="${contactData.name}">のように、ユーザーには見えないhiddenタイプの入力欄があることです。これは、確認画面で表示されたデータを「送信する」ボタンが押されたときに、次の送信処理(ContactControllersubmitContactメソッド)へ確実に引き継ぐために使われます。 「送信する」ボタンを押すと、th:action="@{/contact/submit}"に設定されたURLへデータが送られ、メール送信処理が行われます。 「戻る」ボタンを押すと、/contactのURLへ移動し、入力フォームに戻ります。この際、@SessionAttributes("contactData")の設定により、入力内容がセッションに残っているため、再度入力する必要がありません。

変更点: お問い合わせ送信完了画面のHTMLテンプレートを作成しました

/src/main/resources/templates/contact/complete.html
1+<!DOCTYPE html>
2+<html layout:decorate="layout/layout">
3+<head>
4+    <title layout:fragment="title">お問い合わせ送信完了</title>
5+</head>
6+
7+<main layout:fragment="content">
8+    <div class="container mt-5 text-center">
9+        <h1 class="mb-4">お問い合わせありがとうございました!</h1>
10+        <p>お問い合わせ内容は正常に送信されました。</p>
11+        <a href="/" class="btn btn-primary">トップページに戻る</a>
12+    </div>
13+</main>
14

この変更では、contact/complete.htmlという新しいHTMLテンプレートファイルを作成しました。これは、お問い合わせの送信が完了したことをユーザーに知らせる画面です。 この画面も共通レイアウトを使用しており、送信完了のメッセージと、トップページへ戻るためのリンクがシンプルに表示されています。ユーザーはここから再度トップページへ移動することができます。

変更点: ナビゲーションバーにお問い合わせリンクを追加しました

/src/main/resources/templates/layout/layout.html
1                       <button type="submit" class="nav-link active">Logout</button>
2                     </form>
3                   </li>
4+                  <li class="nav-item">
5+                    <a class="nav-link" href="/contact">お問い合わせ</a>
6+                  </li>
7                 </ul>
8               </div>
9             </div>
10

この変更では、layout/layout.htmlという共通のHTMLレイアウトファイルに、新しいリンクを追加しました。 このlayout.htmlファイルは、Webサイトのヘッダーやフッター、ナビゲーションバーなど、複数のページで共通して表示される部分を定義しています。 今回追加されたのは、ナビゲーションバーの中に「お問い合わせ」へのリンクです。<a class="nav-link" href="/contact">お問い合わせ</a>と記述することで、ユーザーがどのページにいても簡単にクリック一つでお問い合わせフォームのページ(/contact)にアクセスできるようになりました。

おわりに

この記事では、Spring Bootで入力、確認、完了という一連の流れを持つお問い合わせフォームを実装しました。 spring-boot-starter-mailの追加やEmailServiceの作成、application.propertiesでの詳細な設定を通じて、メール送信機能をアプリケーションに組み込む方法を学びました。 また、Spring Securityでフォームへのアクセス権限を許可し、Thymeleafで作成した入力・確認・完了の各画面を連携させることで、使いやすいユーザーインターフェースを構築しました。 これらの実践的な内容を通して、Webアプリケーション開発におけるフロントエンドとバックエンドの連携、さらには外部サービスとの連携といった基礎的なスキルを習得できたことと思います。 ぜひ今回の経験を活かして、さらに複雑な機能を持つWebアプリケーションの開発にも挑戦してみてください。

関連コンテンツ

関連IT用語