Validation
Validationとはフォームで送られたデータを検証する機能です。
フォームのあるシステムでは必ず実装されている機能で、不正なデータによる予期しない挙動を防ぐものです。
先ほど作成したCREATEとUPDATEに実際に実装してみましょう。
/demo/src/main/java/com/example/demo/TodoController.java
package com.example.demo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TodoController {
private final TodoRepository repository; // final修飾子のフィールド変数
// コンストラクタ
public TodoController(TodoRepository repository) {
this.repository = repository;
}
@GetMapping() // GETリクエスト処理
public ModelAndView index () {
// テンプレートで利用するデータと、Viewに関する情報を管理するオブジェクト
ModelAndView mav = new ModelAndView();
+
+ // Entityインスタンス
+ Todo todo = new Todo();
+
+ // エラーメッセージ表示のため追加
+ mav.addObject("todo", todo);
+
// keyがtodosのViewで使用できるデータを追加
mav.addObject("todos", repository.findAll());
// Viewファイル名を設定
mav.setViewName("index");
return mav;
}
@PostMapping() // POSTリクエスト処理
+ // フォームのデータ検証結果を保持するオブジェクト
- public String store (Todo todo) {
+ public String store (@Validated Todo todo, BindingResult result, Model model) {
+ if (result.hasErrors()) {
+ /* keyがtodosのViewで使用できるデータを追加
+ *戻り値がStringのためModelで対応
+ */
+ model.addAttribute("todos", repository.findAll());
+ return "index";
+ }
+
// 送られてきたformDataデータを保存
repository.save(todo);
// indexにリダイレクト
return "redirect:/";
}
@GetMapping("/{id}")
// パスパラメータを受け取るアノテーション
public ModelAndView edit (@PathVariable Long id) {
ModelAndView mav = new ModelAndView();
mav.addObject("todo", repository.findById(id));
mav.setViewName("edit");
return mav;
}
// わかりやすくstoreメソッドと分けていますが、一緒でも動作します。
@PostMapping("/{id}")
+ // フォームのデータ検証結果を保持するオブジェクト
- public String update (Todo todo) {
+ public String update (@Validated Todo todo, BindingResult result) {
+ if (result.hasErrors()) {
+ return "edit";
+ }
+
repository.save(todo);
return "redirect:/";
}
@PostMapping("/delete/{id}")
// パスパラメータを受け取るアノテーション
public String delete (@PathVariable Long id) {
repository.deleteById(id);
return "redirect:/";
}
}
/demo/src/main/java/com/example/demo/Todo.java
package com.example.demo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
@Entity // エンティティを示すクラスに付けるアノテーション
@Table(name = "\"todos\"") // テーブルを指定するアノテーション
public class Todo {
@Id // エンティティクラスのフィールドをデータベースのプライマリーキー
@GeneratedValue(strategy = GenerationType.IDENTITY) // 自動でIDを生成
private Long id;
@Column // DBカラムマッピング
+ @NotBlank
+ @Size(min=1, max=10)
private String title;
@Column // DBカラムマッピング
+ @NotBlank
+ @Size(min=1, max=30)
private String memo;
/*
* getter・setter
* getter : フィールドの値を取り出す
* setter : フィールドに値を設定する
*/
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
}
/demo/src/main/resources/templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>一覧画面</title>
<link th:href="@{/css/style.css}" rel="stylesheet">
</head>
<body>
<div class="container">
<h2>一覧画面</h2>
- <form method="post" action="/">
+ <form method="post" action="/" th:object="${todo}">
<input type="hidden" th:field="*{id}">
<input type="text" placeholder="タイトルを入力..." th:field="*{title}">
<br>
+ <div class="text-error" th:if="${#fields.hasErrors('title')}"
+ th:errors="*{title}" style="color:red"></div>
<br>
<textarea rows="4" placeholder="メモを入力..." th:field="*{memo}"/>
<br>
+ <div class="text-error" th:if="${#fields.hasErrors('memo')}"
+ th:errors="*{memo}" style="color:red"></div>
<br>
<button type="submit">追加</button>
</form>
<table>
<thead>
<tr>
<th>#</th>
<th>タイトル</th>
<th>メモ</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="todo : ${todos}">
<td th:text="${todo.id}"></td>
<td th:text="${todo.title}"></td>
<td th:text="${todo.memo}"></td>
<td><a th:href="@{/} + ${todo.id}"><button>編集</button></a></td>
<td>
<form method="post" th:action="@{/delete/{id}(id=${todo.id})}">
<button type="submit">削除</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
/demo/src/main/resources/templates/edit.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>編集画面</title>
<link th:href="@{/css/style.css}" rel="stylesheet">
</head>
<body>
<div class="container">
<h2>編集画面</h2>
<form method="post" th:action="@{/*{id}}" th:object="${todo}">
+ <input type="hidden" th:field="*{id}">
<input type="text" placeholder="タイトルを入力..." th:field="*{title}">
<br>
+ <div class="text-error" th:if="${#fields.hasErrors('title')}"
+ th:errors="*{title}" style="color:red"></div>
<br>
<textarea rows="4" placeholder="メモを入力..." th:field="*{memo}"></textarea>
<br>
+ <div class="text-error" th:if="${#fields.hasErrors('memo')}"
+ th:errors="*{memo}" style="color:red"></div>
<br>
<button type="submit">編集</button>
</form>
</div>
</body>
</html>
アプリケーションを実行します。
動作確認して終了です。
ここまでの内容で簡単なアプリケーションは作成できるようになります。