かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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>