Spring Bootが提供するバリデーションの使用方法

依存関係の追加

spring-boot-starter-validationの依存関係を追加する。
gradleを使用している場合、以下の記述をbuild.gradleに追加する。

// Hibernate Validator で Java Bean Validation を使用するためのスターター
 implementation 'org.springframework.boot:spring-boot-starter-validation'

アノテーションの付加

バリデーションをしたいフィールドにアノテーションを付与する。
4つのフィールドに下表のような入力チェックを適用するためにアノテーションを付与する例を示す。

フィールド チェック項目
name 必須チェック
mailAddress 必須チェック
メールアドレスチェック
age 範囲チェック(0から100歳まで)
birthday 過去日チェック
import java.time.LocalDate;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Past;

import org.hibernate.validator.constraints.Range;

import lombok.Data;

/**
 * サンプルModel。<br>
 */
@Data
public class SampleModel {

   /** 名前 */
   @NotBlank
   private String name;

   /** メールアドレス */
   @Email
   @NotBlank
   private String mailAddress;

   /** 年齢 */
   @Range(min = 0, max = 100)
   private int age;

   /** 誕生日 */
   @Past
   @DateTimeFormat(pattern = "yyyy-MM-dd")
   private LocalDate birthday;
}

バリデーションの実行

バリデーション実行方法を2種類以下に示す。

リクエストを受け取ったタイミングで実行(@Validated)

コントローラーで@Validatedを付与することで、入力値のバリデーションが実行される。
バリデーションの結果はBindingResultに入るため、メソッドの引数に追加する。

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.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class SampleController {

    @RequestMapping(value = "/result", method = RequestMethod.POST)
    public String result(@ModelAttribute @Validated SampleModel sm, BindingResult result, Model model) {
        if (result.hasErrors()) {
            model.addAttribute("sm", sm);
            return "index";
        }
        return "result";
    }
}

任意のタイミングで実行(SmartValidatorの使用)

コンストラクタインジェクションでSmartValidatorをDIし、validateメソッドを呼び出すことでバリデーションが実行される。
エラーメッセージを受け取る場合はBindingResultをメソッドの引数に追加する。

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.SmartValidator;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class SampleController {

    public SmartValidator validator;

    public ValidationSampleController(SmartValidator validator) {
        this.validator = validator;

    }

    @RequestMapping(value = "/validationResult", method = RequestMethod.POST)
    public String result(@ModelAttribute ValidationSampleModel vm, BindingResult result,
            Model model) {
        validator.validate(vm, result);
        if (result.hasErrors()) {
            model.addAttribute("validationSampleModel", vm);
            return "validationSample";
        }
        return "validationResult";
    }
}

エラーメッセージの画面表示(Thymeleaf使用の場合)

エラーメッセージは以下の記述で表示される。

th:errors="*{バリデーションNGだったフィールド名}"

@Validated が付与されたインスタンスの指定も必要であり、formタグにth:object="${インスタンス名}" 属性を追加する。
指定するインスタンス名は、コントローラクラスでmodel.addAttribute("sm", sm);の記述で設定した名前(sm)ではなく、
Spring内で自動的に解決した名前となるため、留意する。参考

 <!DOCTYPE html>
 <html xmlns:th="<http://www.thymeleaf.org>">
   <head>
     <meta charset="UTF-8">
     <title>index</title>
   </head>
   <body>
     <h2 th:text="Home"></h2>

     <form th:action="@{/result}" th:object="${sampleModel}" method="post">
       <div>名前</div>
       <div><input type="text" th:field="*{name}" /></div>
       <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" style="color: red"></span>

       <div>メールアドレス</div>
       <div><input type="text" th:field="*{mailAddress}" /></div>
       <span th:if="${#fields.hasErrors('mailAddress')}" th:errors="*{mailAddress}" style="color: red"></span>

       <div>年齢</div>
       <div><input type="number" th:field="*{age}" /></div>
       <span th:if="${#fields.hasErrors('age')}" th:errors="*{age}" style="color: red" ></span>

       <div>誕生日</div>
       <div><input type="date" th:field="*{birthday}" /></div>
       <span th:if="${#fields.hasErrors('birthday')}" th:errors="*{birthday}" style="color: red"></span>

       <p><input type="submit" value="送信" /></p>
     </form>
   </body>
 </html>

バリデーションのエラーメッセージの変更

バリデーションエラー時のメッセージは特に設定のない場合、デフォルトで設定されているメッセージとなる。
独自のメッセージに変更する方法を以下に示す。

1. 特定のクラスの特定のバリデーションのメッセージを変更する場合は、付与したアノテーションmessage属性を追加する。

/** 名前 */
@NotBlank(message = "必須入力です")
private String name;

2. 特定のバリデーションのデフォルトメッセージを変更する場合はsrc/main/resourceにValidationMessages.propertiesを作成する。
以下は@NotBlankのデフォルトメッセージを変更する際の記述例である。
入力された値をメッセージに表示したい場合は${validatedValue}を使用する。
{0}でエラーしたフィールド名が取得できる。

javax.validation.constraints.NotBlank.message=必須入力です
# 「2030-12-31は未来日です。過去の日付を入力してください。」と表示される
 javax.validation.constraints.Past.message=${validatedValue}は未来日です。過去の日付を入力してください。

3. 特定のアノテーションのメッセージを変更する場合はsrc/main/resourceにmessages.propertiesを作成する。
{0}でエラーしたフィールド名が取得できる。
エラーしたフィールド名を日本語で表示したい場合も設定可能。

# 「nameは必須入力です」と表示される
NotBlank={0}は必須入力です
# 「名前は必須入力です」と表示される
name=名前
NotBlank={0}は必須入力です