かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Spring Bootで入力値の検証

Spring Bootで入力値の検証をします。

基本

Bean Validationが使えるみたいです。

Using Bean Validation - The Java EE 6 Tutorial

ということで、以下のようなクラスを作ってアノテーションをつけるだけでOK。

package helloboot.modelattr.controllers;

import javax.validation.constraints.Pattern;

import org.hibernate.validator.constraints.NotEmpty;

public class PersonForm {
    @NotEmpty(message = "{name}")
    private String name;
    @NotEmpty(message = "{age}")
    @Pattern(regexp = "[0-9]*", message="数字で入力してください")
    private String age;

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

}

messageにエラーメッセージを指定するのですが、このとき普通に文字列を渡してもいいし{key}のような書き方をするとValidationMessages.propertiesからエラーメッセージをひっぱってくるようにもできます。国際化も楽ちんですね。

因みにValidationMessages.propertiesは以下のような感じにしました。

age=age error!
name=name error!

コントローラでのエラーハンドリング

コントローラでバリデーションの結果を受け取るには以下のようにします。

  • @ValidアノテーションをFormクラスにつける
  • BindingResultインターフェースを@Validを付けたパラメータのすぐ後ろに追加する

メソッドの定義はこんな感じ。

public String post(@ModelAttribute("form") @Valid PersonForm form, BindingResult bindingResult)

bindingReusltのhasErrorsメソッドでエラーの有無が確認できます。フィールド単位のエラーが欲しかったら、hasFieldErrorsを使います。getAllErrors, getFieldErrorsでエラーを取得します。getAllErrorsやgetFieldErrorsメソッドの戻り値はList>ObjectError<でgetDefaultMessageでエラーメッセージを取得できます。

こんな感じで。

@RequestMapping(name = "/", method = { RequestMethod.POST })
public String post(@ModelAttribute("form") @Valid PersonForm form, BindingResult bindingResult) {
    for (ObjectError error : bindingResult.getAllErrors()) {
        System.out.println(error.getDefaultMessage());
    }
    return "index";
}

Thymeleafでのエラーの処理

最終的に画面でエラーを出す方法は以下のような感じです。

#fieldsを使ってエラー情報を取得します。detailedErrorsですべてのエラーを取得できます。こんな感じで。th:objectでエラーをとりたいオブジェクトを設定してる点ポイントです。

<body th:object="${form}">
    <ul>
        <li th:each="error : ${#fields.detailedErrors()}">
            <span th:text="${error.message}">Error message</span>
        </li>
    </ul>
    ...
</body>

次に入力フィールドの横とかにエラーを出したい場合の書き方です。

<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name error</span>

エラーの時だけスタイルを切り替えたいときとかはth:errorclassを使います。

</label><input name="name" type="text" th:field="*{name}" th:errorclass="error-field"/>

エラーが1つもないときだけ表示したい項目は以下のようにかけました。(もっとスマートな書き方があるかも)

<div th:if="${#arrays.length(#fields.detailedErrors())} == 0">
    ...ここにエラーがないときに出したい項目を書く...
</div>