【Java】Spring Boot 2.3.1でバリデーションをつけていく記事

## はじめに

こんにちは！ゼロからJavaであそぶシリーズ第4弾です。いま名前つけました。

前回までの記事はこちら↓

1. [macでSTSの環境構築](https://qiita.com/akane_kato/items/fff46dfa34f97f95dc88)
2.  [Spring BootでHelloWorld](https://qiita.com/akane_kato/items/a59494b0260f0e648425)
3.  [Spring Bootでエコーアプリケーション作成](https://qiita.com/akane_kato/items/125b87446a5fe56b4f50)

今回は、前回作ったエコーアプリケーションに、バリデーションをつけていこうと思います。

![もょもと.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/cb206e00-8fdf-7b1b-926e-97b5a7298ed4.gif)

現状、文字列も数字も環境依存文字も、何文字でも入れられてしまうガバ状態なので、怪文書を送ると以下のように表示崩れが発生してしまいます。

<img width="619" alt="スクリーンショット 2020-07-21 0.26.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/97732387-8220-bbfc-c415-f41bffe734ee.png">

怖い。
名前にハテナが並んでると不穏な雰囲気になっちゃいますね。文字化けかな？
ゼロが画面幅を突き破ってしまっているのもなんか嫌ですね。

そこで今回は、何かのサービスのユーザーネームっぽく、以下の要件でバリデートするようにしちゃおうと思います。エラーの場合同じ画面内でエラーメッセージを出すイメージです。

- 使用可能文字種：半角英数のみ
- 文字数制限：4文字以上

完成品は以下のような形です。
（背景色を目に優しい色に変えました）

![画面収録 2020-07-21 23.39.15.mov.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/30f2b2ed-ddc7-61f1-e50b-d7b30ca291e4.gif)

### 使用環境とバージョン
- macOS Catalina
- jdk14.0.1
- JUnit5
- Maven 3.6.3_1
- STS 4.6.1
- Spring Boot 2.3.1
- spring-boot-starter-thymeleaf-2.3.1

## フォームクラスにアノテーションでバリデーションを実装
フォームクラスに以下のアノテーションを追加しました。

- `@NotBlank`：nullまたは空文字でないこと[^1]をチェックできます
- `@Size`：属性minまたはmaxを使用して最小値・最大値を指定できます
- `@Pattern`：属性値regexpで正規表現のパターン文字列を指定できます。今回は半角英数のみを許容する正規表現を使用しました

[^1]: nullのみチェックするには@NotNullを使用します

```java:EchoForm.java
package com.example.form;

import java.io.Serializable;

import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.NotBlank;

public class EchoForm implements Serializable {
    private static final long serialVersionUID = 1L;
    @NotBlank
    @Size(min = 4)
    @Pattern(regexp = "[a-zA-Z0-9]*") //英数字であること
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
```
## コントローラークラスにエラー時の分岐追加
変更を加えたechoメソッドのみ記述します。
変更点は以下2点です。

- `引数にBindingResultクラスを追加`しました。BindingResultには入力チェック結果が入っています。入力チェックの実行とBindingResultの生成はフレームワークが担ってくれる部分ですので、実装の必要はありません。（[Spring徹底入門](https://www.shoeisha.co.jp/book/detail/9784798142470)より）
- `エラー時の分岐を追加`しました。
アノテーションによるチェックでエラーが発生した場合、「Hello ${ユーザーネーム}」の画面に行ってほしくないため、同じページをリターンするように変更しています。

```java:EchoController.java
@RequestMapping(value = "echo", method = RequestMethod.POST)
	public String echo(@Validated EchoForm echoForm, BindingResult result, Model model) {
		if (result.hasErrors() ) {
			model.addAttribute("name", echoForm.getName());
			return "index";
		}
		model.addAttribute("name", echoForm.getName());
		return "echo";
	}
```
## index.htmlにエラー時のテキスト追加
エラーが出た際だけ、以下のように赤字のエラーメッセージを表示するようにしたいんですよね…
これ、Springならできちゃいます！

<img width="402" alt="スクリーンショット 2020-07-21 23.26.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/e39e8dc3-091a-0742-28f7-5ed851434213.png">

前回既にフォームオブジェクトをバインディングしているので、echoFormのエラー情報を取得してThymeleafにぶち込みHTMLを作ることができてしまうのです。
Springすごい。Thymeleafすごい。

form部分のみ記載します。
spanで囲っている場所で、オブジェクトがエラーである場合のみエラーメッセージを表示するようにしてあります[^2]。具体的に言うと、th:errors属性にフォームオブジェクトのプロパティを設定しているため、指定したプロパティに対するエラーメッセージのみ表示されるようになっています。
エラーメッセージが複数ある場合は自動的に`<br />`が入る仕様です。ありがたい。

[^2]: [こちらのページ](https://takeda-san.hatenablog.com/entry/2018/01/18/224244)の実装を参考にしました。

```html:index.html
<form th:object="${echoForm}" th:action="@{/echo}" method="POST">
        <div>ユーザーネームを入れてください</div>
        <div>（半角英数4文字以上）</div>
        <div>
        	<input type="text" th:field="*{name}" /> 
        	<button>送信します</button>
        </div>
        <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" style="color: red"></span>
</form>
```


## エラー文言の日本語化
実は、ここまでの実装で実行すると、エラーメッセージがアノテーションのデフォルトメッセージ（英語）になってしまいますので、エラーが以下のようになってしまいます。

<img width="432" alt="スクリーンショット 2020-07-21 23.52.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/a1a9f293-c4bc-6359-6f58-af6830a60dac.png">

これはちょっと嫌ですね…上はまだオシャレなエラーメッセージ❤️で通せるかもですけど、sizeの数字に関してはユーザーから見たら意味不明ですものね…
ここは日本語のエラーメッセージに直しましょう！

Spring MVCによるBean Validationのエラーメッセージは、propertiesファイルに定義するのが望ましいようです。いくつか方法があるようですが、クラスパス直下にValidationMessages.propertiesを作成する方法が分かりやすかったので、そちらを採用しました。

resources配下にValidationMessages.propertiesを新規作成します。

<img width="306" alt="スクリーンショット 2020-07-22 0.29.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/5aadf94e-3874-78e2-d590-ce809170d3f4.png">

中身は、使用したアノテーションのメッセージを書き換えるだけのシンプルな実装です。

```ValidationMessages.properties
org.hibernate.validator.constraints.NotBlank.message=入力必須です。
javax.validation.constraints.Size.message=4文字以上でなくてはいけません。
javax.validation.constraints.Pattern.message=半角英数のみ有効です。
```

ちなみに、上記をただValidationMessages.propertiesに貼り付けると、日本語入力してエンターを押すだけで自動的に以下のようになってしまいました…Spring Bootの標準BeanがUTF-8でないため[マルチバイト文字で文字化けしてしまうようです](https://fumidzuki.com/knowledge/210/)。

```ValidationMessages.properties
org.hibernate.validator.constraints.NotBlank.message=\u5165\u529B\u5FC5\u9808\u3067\u3059\u3002
javax.validation.constraints.Size.message=4\u6587\u5B57\u4EE5\u4E0A\u3067\u306A\u304F\u3066\u306F\u3044\u3051\u307E\u305B\u3093\u3002
javax.validation.constraints.Pattern.message=\u534A\u89D2\u82F1\u6570\u306E\u307F\u6709\u52B9\u3067\u3059\u3002
```

[こちらのQiita](https://qiita.com/yukina-ge/items/b4303920c60ec3251d37)のコメント欄でも言及があったのですが、自分もSpring 2系でJava11以上だからか、ファイルのプロパティでUTF-8にすればアプリケーションでも問題なく作動しました。
ファイルを右クリック > プロパティ > ResourceのText file encodingをデフォルトからUTF-8に変更するだけです。

<img width="877" alt="スクリーンショット 2020-07-22 0.58.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/1f1ee5ee-c546-2ae4-26cf-851bece3b94c.png">

これでファイルを保存しようとすると以下警告が出ますが、`Save as UTF-8`で問題なく作動しました。

<img width="591" alt="スクリーンショット 2020-07-22 0.50.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/33411c30-4534-bdc9-d6ca-298c516c21c8.png">

これで、以下のように日本語のメッセージになりました！

<img width="428" alt="スクリーンショット 2020-07-22 0.41.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/b77f8f7c-9fb4-ef93-63b8-fa3b2deb0617.png">

## おわりに
今回はエラー時のテキスト追加部分のThymeleaf記法と、ValidationMessages.propertiesの日本語化でちょっと苦労しました！今回紹介した方法はSpring 1系だと怪しいので、適宜調べてみてください。

お読みいただき、ありがとうございました！