フォーム入力の基本(th:field)を完全ガイド!初心者でもわかるフォーム作成方法
新人
「Springでフォームを作りたいのですが、どうやって入力値を受け取ればいいですか?」
先輩
「Spring MVCでは、Thymeleafと一緒にth:fieldを使うことで簡単にフォーム入力を扱えるんだ。」
新人
「th:fieldって何ですか?どんなふうに使うんですか?」
先輩
「じゃあ、基本的なフォームの作り方とth:fieldの使い方を説明するね!」
1. フォーム入力とは?
フォーム入力は、ユーザーから情報を受け取るためのWebページ上の仕組みです。例えば、名前やメールアドレスを入力する欄があるフォームがあります。Spring MVCでは、Thymeleafを使って簡単にフォームを作成し、サーバーにデータを送信できます。
2. th:fieldとは何か?
th:fieldは、Thymeleafでフォームとオブジェクトのプロパティをバインドするための属性です。これを使うことで、入力欄とJavaのモデルオブジェクトを簡単に連携させることができます。
例えば、ユーザーの名前を入力する場合、以下のように記述します。
<form th:action="@{/submit}" th:object="${user}" method="post">
<label for="name">名前:</label>
<input type="text" th:field="*{name}" id="name" />
<button type="submit">送信</button>
</form>
上記のコードでは、th:objectでuserオブジェクトを指定し、th:field="*{name}"でオブジェクトのnameプロパティとバインドしています。
3. フォームとコントローラの基本的な連携方法
フォームで入力されたデータをサーバー側で受け取るには、@Controllerクラスを用意します。以下は基本的な連携方法の例です。
@Controller
public class UserController {
@GetMapping("/form")
public String showForm(Model model) {
model.addAttribute("user", new User());
return "form";
}
@PostMapping("/submit")
public String submitForm(@ModelAttribute User user, Model model) {
model.addAttribute("submittedUser", user);
return "result";
}
}
この例では、/formにアクセスすると空のUserオブジェクトをビューに渡してフォームを表示します。/submitにデータが送信されると、@ModelAttributeを使ってフォームのデータをUserオブジェクトとして受け取ります。
ユーザークラスは以下のように作成します。
public class User {
private String name;
// GetterとSetter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
送信後に表示されるresult.htmlは以下の通りです。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>結果画面</title>
</head>
<body>
<h2>送信結果</h2>
<p>名前: <span th:text="${submittedUser.name}"></span></p>
</body>
</html>
4. th:fieldを使ったフォーム作成の実例
ここでは、実際にth:fieldを使ってユーザー情報を入力するフォームを作成します。この例では、名前とメールアドレスを入力し、サーバーに送信します。
<form th:action="@{/submit}" th:object="${user}" method="post">
<div>
<label for="name">名前:</label>
<input type="text" th:field="*{name}" id="name" placeholder="名前を入力" />
</div>
<div>
<label for="email">メールアドレス:</label>
<input type="email" th:field="*{email}" id="email" placeholder="example@example.com" />
</div>
<button type="submit">送信</button>
</form>
このフォームでは、th:objectでuserオブジェクトをバインドし、それぞれの入力欄でth:fieldを使ってnameとemailプロパティに連携させています。
5. 入力データのバリデーション(基本的なバリデーションの実装)
ユーザーが間違ったデータを入力しないようにするために、Spring MVCではバリデーションを実装できます。@NotBlankや@Emailアノテーションを使うことで、簡単に入力チェックが可能です。
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
public class User {
@NotBlank(message = "名前は必須です。")
private String name;
@NotBlank(message = "メールアドレスは必須です。")
@Email(message = "有効なメールアドレスを入力してください。")
private String email;
// GetterとSetter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
次に、バリデーションを適用するためにコントローラを更新します。
import jakarta.validation.Valid;
import org.springframework.validation.BindingResult;
import org.springframework.ui.Model;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class UserController {
@GetMapping("/form")
public String showForm(Model model) {
model.addAttribute("user", new User());
return "form";
}
@PostMapping("/submit")
public String submitForm(@Valid @ModelAttribute User user, BindingResult result, Model model) {
if (result.hasErrors()) {
return "form"; // エラーがあればフォーム画面に戻る
}
model.addAttribute("submittedUser", user);
return "result"; // 成功時の画面
}
}
フォームに戻る際にエラーメッセージを表示するには、HTMLに以下を追加します。
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>
6. フォーム送信後のデータ処理と表示方法
フォームが正常に送信されると、入力データを結果画面に表示できます。以下がresult.htmlの内容です。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>送信結果</title>
</head>
<body>
<h2>送信結果</h2>
<p>名前: <span th:text="${submittedUser.name}"></span></p>
<p>メールアドレス: <span th:text="${submittedUser.email}"></span></p>
<a th:href="@{/form}">戻る</a>
</body>
</html>
これにより、ユーザーが入力した名前とメールアドレスが確認できます。また、戻るボタンを押すことで再度フォーム画面に戻れます。
7. よくあるエラーとその対処法
フォーム入力を扱う際には、初心者がつまずきやすいエラーがいくつかあります。ここでは、よくあるエラーとその解決方法を解説します。
エラー1: フォームデータがバインドされない
原因の一つは、th:objectとth:fieldが正しく連携していない場合です。例えば、th:field="*{name}"を指定しているのに、モデルにuserオブジェクトが渡されていないとエラーになります。
// 正しい例
@GetMapping("/form")
public String showForm(Model model) {
model.addAttribute("user", new User());
return "form";
}
エラー2: バリデーションメッセージが表示されない
バリデーションエラーがあっても、th:errorsを設定していないとメッセージが表示されません。以下のようにフォーム内にエラーメッセージ用のタグを追加してください。
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>
エラー3: MethodArgumentNotValidExceptionが発生する
これは、@ValidやBindingResultの順序が間違っている場合に発生します。@Validは@ModelAttributeの直前に置き、BindingResultは直後に配置してください。
@PostMapping("/submit")
public String submitForm(@Valid @ModelAttribute User user, BindingResult result, Model model) {
if (result.hasErrors()) {
return "form";
}
model.addAttribute("submittedUser", user);
return "result";
}
8. th:fieldを活用する上での注意点とベストプラクティス
th:fieldを使用する際には、いくつかのポイントに注意することで、より安全で保守性の高いコードになります。
ポイント1: 入力値のサニタイズ
ユーザーからの入力値は必ずサニタイズし、表示時にはエスケープ処理を行いましょう。Thymeleafではth:textを使うことで自動的にエスケープ処理がされます。
ポイント2: ネストしたオブジェクトのバインド
オブジェクト内に別のオブジェクトを持つ場合、以下のようにth:fieldでネストしたプロパティも扱えます。
<input type="text" th:field="*{address.street}" placeholder="住所" />
ポイント3: フォーム送信時のセキュリティ
CSRF対策として、Spring Securityを導入している場合はフォーム内に以下のタグを追加してください。
<input type="hidden" th:name="_csrf" th:value="${_csrf.token}" />
9. 実践!簡単なユーザー登録フォームの作成例
最後に、これまでの内容をまとめた簡単なユーザー登録フォームを作成してみましょう。
ユーザークラス
public class User {
@NotBlank(message = "名前は必須です。")
private String name;
@NotBlank(message = "メールアドレスは必須です。")
@Email(message = "有効なメールアドレスを入力してください。")
private String email;
private String address;
// GetterとSetter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
}
コントローラクラス
@Controller
public class UserController {
@GetMapping("/register")
public String showForm(Model model) {
model.addAttribute("user", new User());
return "register";
}
@PostMapping("/register")
public String submitForm(@Valid @ModelAttribute User user, BindingResult result, Model model) {
if (result.hasErrors()) {
return "register";
}
model.addAttribute("submittedUser", user);
return "success";
}
}
register.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>ユーザー登録</title>
</head>
<body>
<h2>ユーザー登録フォーム</h2>
<form th:action="@{/register}" th:object="${user}" method="post">
<div>
<label for="name">名前:</label>
<input type="text" th:field="*{name}" placeholder="名前を入力" />
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
</div>
<div>
<label for="email">メールアドレス:</label>
<input type="email" th:field="*{email}" placeholder="メールアドレスを入力" />
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>
</div>
<div>
<label for="address">住所:</label>
<input type="text" th:field="*{address}" placeholder="住所を入力" />
</div>
<button type="submit">登録</button>
</form>
</body>
</html>
success.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>登録完了</title>
</head>
<body>
<h2>登録が完了しました!</h2>
<p>名前: <span th:text="${submittedUser.name}"></span></p>
<p>メールアドレス: <span th:text="${submittedUser.email}"></span></p>
<p>住所: <span th:text="${submittedUser.address}"></span></p>
<a th:href="@{/register}">再登録</a>
</body>
</html>
この例では、名前、メールアドレス、住所を入力し、バリデーションエラーがあればフォームに戻り、正しく送信されれば登録完了画面が表示されます。PleiadesでGradleを使用してプロジェクトを作成し、依存関係をチェックで追加することで簡単に動作確認が可能です。