【Spring Boot入門】#3 バリデーション（入力チェック）

## はじめに
自身の知識のアウトプットも兼ねて、新人研修用に作成した記事となります。Spring Bootを学び始めた方を対象とした内容になっています。

登録画面の表示～登録機能の作成については、前回の記事を参照ください。

https://qiita.com/ioaio/items/ba78d173bdc0e283a34f

## 概要
前回、ユーザーが入力した情報をデータベースに登録するところまで確認しました。

しかし、今のままではフォームから送られてくるデータが適正なものかのチェックが行われていないません。

例えば、従業員数に数字以外の値を登録しようとした場合を見てみましょう。DB上は従業員数を数値型で管理している為、この状態で登録しようとすると例外がスローされます。（`java.lang.NumberFormatException` はアプリケーションが文字列を数値型に変換しようとしたとき、文字列の形式が正しくない場合にスローされます。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3917178/dddbbeab-739a-afd0-33b0-f64ac29d7898.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3917178/cc0f2066-954b-e6f3-03b9-3d2256d7a606.png)

#### バリデーションチェック（入力チェック）
不正なデータがDBに登録されたりするのを防ぐために、フォームから送られてくるデータの値や型を検査して、不正なデータがあれば棄却するようにする仕組みがバリデーション（Validation）です。

## Formクラスの修正
ユーザーから入力されたデータは先ずFormクラスに入ってきます。なので、この段階で入力データのチェックを行うのが望ましいです。
`配置先：src > main > java > com > example > demo > controller > form > CompanyForm.java`

```java:CompanyForm.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CompanyForm {

	@NotBlank(message = "会社名は必須です")
	@Size(max = 255)
	private String companyName;	// 会社名
	
	@NotBlank(message = "従業員数は必須です")
	@Min(value = 0, message = "従業員数は0以上の数字を入力してください")
	private String employees;	// 従業員数
	
	@NotBlank(message = "設立日は必須です")
	private String est;	// 設立日
}
```
#### @￰NotBlank
必須チェックのためのアノテーションです。Null、空文字、半角スペースが許可されないことを示します。

#### @￰Size
文字列の長さを管理するアノテーションです。今回の場合は、255文字までの会社名を許可します。

#### @￰Min
データ型の値の最小値を管理するアノテーションです。

この他にもSpringには入力チェックに使える標準アノテーションが色々用意されています。中でも良く利用されるのは`@￰Pattern`です。

#### @￰Pattern
特定の正規表現パターンに基づいてフィールドの値を検証するために利用されるアノテーションです。（通常はString型のデータに対して使用されます）これにより、ユーザーの入力したデータが期待される形式に合致しているかどうかを確認することができます。

このアノテーションには、以下の主要な属性があります。
`regexp`：検証に使用する**正規表現**を指定します。
`message`：検証エラー時に表示されるメッセージを指定します。

```java:Example
import javax.validation.constraints.Pattern;

public class UserForm {
    @Pattern(regexp = "^[a-zA-Z0-9]{5,10}$", message = "ユーザー名は5〜10文字の英数字でなければなりません")
    private String username;
}
```

## Controllerクラスの修正
Controllerクラスに入力チェックの処理を追加します。
`配置先：src > main > java > com > example > demo > controller > CompanyController.java`

```java:CompanyController.java
@Controller
public class CompanyController {

	// 登録フォームの表示
	
	// 企業情報の新規登録
	@PostMapping("company/register")
	public String addCompany(@ModelAttribute @Validated CompanyForm companyForm,
			BindingResult bindingResult,	// 追加
			Model model) {
		
		// 追加
		if(bindingResult.hasErrors()) {
			return "register";
		}
		
		companyService.createCompany(companyForm);
		return "thanks";
	}
}
```
#### 引数の追加
addCompanyメソッドの引数を追加します。
・**`@Valid CompanyForm companyForm`**：
これは、送信されたフォームデータが CompanyForm クラスにバインドされた状態で渡されます。`@Valid`アノテーションにより、CompanyForm クラスで定義されたバリデーションルールが自動的に適用されます。

・**`BindingResult bindingResult`**：
これは、フォームデータのバリデーション結果を保持するオブジェクトです。

#### メソッド処理の追加
**`bindingResult.hasErrors()`** を呼び出し、バリデーションエラーがあるかどうかをチェックします。

エラーがある場合は、文字列"register"を返します。これにより、ユーザーは元のフォームテンプレートに戻されます。エラーがない場合は、そのまま登録処理へと進みます。

## Viewの修正
バリデーションエラーがある場合にそのエラーメッセージが表示されるように、register.htmlに追加します。
`配置先：src > main > resources > templates > register.html`

```html:register.html
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>新規登録画面</title>
</head>
<body>
	<h1>企業情報登録フォーム</h1>
	<form th:action="@{/company/register}" th:object="${companyForm}" method="post">
		<table>
			<tr>
				<td><label for="company">会社名：</label></td>
				<td><input type="text" th:field="*{companyName}" /></td>
                <!-- 追加　ここから -->
				<td th:if="${#fields.hasErrors('companyName')}" th:errors="*{companyName}">Name Error</td>
                <!-- 追加　ここまで -->
			</tr>
			<tr>
				<td><label for="emp">従業員数：</label></td>
				<td><input tyape="text" th:field="*{employees}" /></td>
                <!-- 追加　ここから -->
				<td th:if="${#fields.hasErrors('employees')}" th:errors="*{employees}">Number of Employees Error</td>
                <!-- 追加　ここまで -->
			</tr>
			<tr>
				<td><label>設立日：</label></td>
				<td><input type="date" th:field="*{est}" /></td>
                <!-- 追加　ここから -->
				<td th:if="${#fields.hasErrors('est')}" th:errors="*{est}">Establishment Date Error</td>
                <!-- 追加　ここまで -->
			</tr>
			<tr>
				<td><input type="submit" value="登録する" /></td>
			</tr>
		</table>
	</form>
</body>
</html>
```

#### th:if="${#fields.hasErrors('フィールド名')}"
・この部分は条件分岐です
・`#fields.hasErrors('companyName')` は `companyName`フィールドにエラーが存在するかを返します。
・エラーが存在する場合のみ、この `td` 要素が表示されます

#### th:errors="*{フィールド名}"
・これは実際のエラーメッセージを表示する部分です。
・`companyName`フィールドに関連するエラーメッセージを表示します。
・`*{companyName}` は、`companyName`フィールドのエラー情報にアクセスするための式です。
・バリデーション設定（`@NotBlank` など）で定義したメッセージが表示されます。

#### デフォルトのテキスト
・`Name Error` はフォールバックテキストです。
・Thymeleafが正常に動作しない場合に表示される代替テキストです。

## 動作確認
実装した入力チェックが意図したとおりに動くか確認しましょう。
何も入力しない状態で登録ボタンを押すと…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3917178/0f56f5d0-5921-88ef-5197-c66edd090df1.png)

正常に入力チェックが走り、指定したエラーメッセージが表示されることを確認できました。

次回は検索機能を作成していきます。

## 参考サイト
https://spring.io/guides/gs/validating-form-input