Spring bootでバリデーションチェックによるエラーメッセージを表示させる手順

Spring bootの学習で前回エラーメッセージの表示方法を学んだので、アウトプットも兼ねて投稿します。

手順は以下の通りです。

:::note info
①プロジェクト作成時、validationライブラリを使用する。
:::

:::note info
②modelクラスを作成し、バリデーションチェック用のアノテーションを使用する。
:::

:::note info
③ControllerクラスのPostメソッドで、@Validated、BindingResultを使用する。
:::

:::note info
④エラーメッセージ用のファイルを作成。
:::

:::note info
⑤作成したファイルに表示させたいメッセージを記載。
:::

:::note info
⑥application.propertiesに最後にファイルのルートを記載。
:::


## ①プロジェクト作成時、validationライブラリを使用する。

まず初めに、プロジェクト作成時にライブラリ依存関係の選択で「Validation」を追加する。
![スクリーンショット 2022-03-28 21.25.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754263/f2c597d6-2b58-3df2-3b8d-ec3258feede8.png)

## ②modelクラスを作成し、バリデーションチェック用のアノテーションを使用する
例）
```java
package com.example.demo.model;

import java.math.BigDecimal;
import java.time.LocalDate;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.format.annotation.NumberFormat;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Sample {
	
	//必須入力、文字列が60文字まで
	@NotBlank
	@Size(max = 60)
	private String name;
	
	//必須入力、文字列が254文字まで
	@NotBlank
	@Size(max = 254)
	private String address;
	
	//必須入力、半角数字かハイフン
	@NotBlank
	@Pattern(regexp = "[0-9-]*")
	private String phoneNumber;
	
	//必須乳録、1000円以上、数値へ変換
	@NotNull
	@Min(1000)
	@NumberFormat(pattern = "#,###")
	private BigDecimal price;
	
	//必須入力、日付へ変換
	@NotNull
	@DateTimeFormat(iso = ISO.DATE)
	private LocalDate paymentDeadline;

}
```

### 各アノテーションの解説
◆@NotBlank
空白、空欄などの値が未記入であるかどうかを管理してくれるアノテーション。


◆@NotNull
@NotBlankと同様、空白、空欄などの値が未記入であるかどうかを管理してくれるアノテーション。
@NotBlankとの違いは、スペースなどの空文字を許容するかどうかにある。


◆@Size
文字列の長さを管理してくれるアノテーション。
※今回の場合は60文字まで入力が可能。


◆@NumberFormat,　@DateTimeFormat
値の形式を管理するアノテーション。
@NumberFormat　は数値の形式を、
@DateTimeFomatは日付や時刻などの形式を管理している。

◆@Min,@Max
データ型の値の最大値、最小値を管理するアノテーション。


## ③ControllerクラスのPostメソッドで、@Validated、BindingResultを使用する。
Viewから送られてきたmodelに対し、@Validatedと、BindingResultクラスで管理し、modelクラスで指定した通りになっているか確認。
```java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.demo.model.Sample;

@Controller
public class HomeController {
	
	@GetMapping("/")
	public String index(@ModelAttribute Sample sample) {
		
		return "index";
	}
	
	@PostMapping("/")
	public String confirm(@Validated @ModelAttribute Sample sample, BindingResult result) {
		
		if(result.hasErrors()) {
			return "index";
		}
		
		return "confirm";
	}

}
```
### 解説
```java
@PostMapping("/")
	public String confirm(@Validated @ModelAttribute Sample sample, BindingResult result) {
		
		if(result.hasErrors()) {
			return "index";
		}
		
		return "confirm";
	}
```
@Validatedでviewから送られてきたmodelにエラーがあるかをチェック、BindingResultクラスのhasErrors()にエラーの内容が入るので、
エラーがあればif文で引っかかり、 viewに戻す処理をしている。

※ViewにあたるHTMLのコードは長いので省略。


## ④エラーメッセージ用のファイルを作成、⑤作成したファイルに表示させたいメッセージを記載。
src/main/resources内にエラーメッセージ用のプロパティファイルを、以下のような配置で作成。
![スクリーンショット 2022-03-28 22.30.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754263/e07df17b-4a48-fe52-e22e-d3f4b6ef8db0.png)



①ValidationMessages_ja.properties
```
#個別メッセージ
Pattern.invoice.phoneNumber = {0}は、半角の数字かハイフンで入力してください（例：03-1234-5678）
Min.invoice.price = {0}は、{1}円以上の値を入力してください。

#共通メッセージ
NotBlank = {0}を入力してください。
NotNull = {0}を入力してください
Size = {0}は、{2}文字以上、{1}文字以下で入力してください。
Min = {0}は、{1}文字以上の値を入力してください

#typeMismatch：個別メッセージ
typeMismatch.sample.price = {0}が不正な値です
typeMismatch.sample.paymentdeadline = {0}は、"2022-01-01"形式で入力してください
```

### 解説
◆メッセージを作成する上で、一番優先順位が高く評価される書き方は、
```
アノテーション名.バインド名.フィールド名 = 内容
```
と書く書き方。
他にも沢山あるがここでは省略。

◆{0}には、フィールド名、{1},{2}...の0以降にはアノテーションの引数の値が入る。
例）
```java
@NotBlank
@Size(max = 60, min = 10)
private String name;

//Size = {0}は、{2}文字以上、{1}文字以下で入力してください。
//{0} = address
//{1} = 60
//{2} = 10
```
◆typeMismatchは、
@DateTimeFormatやN@umberFormatで定めた形式通りでないとエラーが出るようになる。

## ⑥application.propertiesに最後にファイルのルートを記載。
最後にapplication.propertiesファイルに下記を記載して完了！

```
spring.messages.basename=i18n/messages,i18n/ValidationMessages
spring.messages.fallback-to-system-locale=false
```

こちらの手順を利用してで問い合わせフォームを作成し、何も入力せず登録を押すと
以下のような形でエラーが表示できます。

![スクリーンショット 2022-03-28 22.38.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754263/7ab92bbd-a8eb-1c4f-8170-ce89c3fd945d.png)

## 補足
{0} に入るフィールド名は、src/main/resources内にmessages.propertiesというファイルを作成し、以下の通りで記載をし配置しておくと変更することもできます。

```
name=お客様名
address=住所
phoneNumber=電話番号
price=金額（円）
paymentDeadline=支払期日
register=登録
confirmationMessage=登録が正常に完了しました。

```

```html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" rel="stylesheet">
<title>Title</title>
</head>
<body class="bg-light">
	<div class="container">
		<div class="row justify-content-center">
			<div class="col-md-8">
				<h4 class="border-bottom my-3" th:text="#{invoice}"></h4>
				<p class="text-danger" th:text="#{confirmationMessage}"></p>

				<div th:object="${invoice}">
					novalidate>
					
					<div class="mb-3">
						<label for="name" th:text="#{name}"></label>
						<input type="text" class="form-control" th:field="*{name}" disabled>
						
					</div>
					
					<div class="mb-3">
						<label for="address" th:text="#{address}"></label>
						<input type="text"class="form-control" th:field="*{address}" disabled>
					</div>
					
					<div class="mb-3">
						<label for="phone" th:text="#{phoneNumber}"></label>
						<input type="tel"class="form-control" th:field="*{phoneNumber}" disabled>
					</div>
					
					<div class="mb-3">
						<label for="price" th:text="#{price}"></label>
						<input type="text"class="form-control" th:field="*{price}" disabled>
					</div>
					
					<div class="mb-3">
						<label for="paymentDeadline" th:text="#{paymentDeadline}"></label>
						<input type="tel"class="form-control" th:field="*{paymentDeadline}" disabled>
					</div>
					
				</div>
			</div>
		</div>
	</div>

<script th:src="@{/webjars/bootstrap/js/bootstrap.bundle.min.js}"></script>
</body>
```

コードはthymeleafを使用しており、
#{項目名}　で表示が可能です。

###参考文献
Spring boot2 入門から基礎まで

https://www.amazon.co.jp/Spring-Boot-2-%E5%85%A5%E9%96%80-%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E5%AE%9F%E6%BC%94%E3%81%BE%E3%81%A7-ebook/dp/B0893LQ5KY