Spring で Bean Validation のエラーメッセージに任意のフィールド名を埋め込む

Bean Validation のエラーメッセージに任意のフィールド名を埋め込む方法を調べたときのメモ。タイトルには Bean Validation と書いていますが、正確には Spring が提供する機能によってエラーメッセージに任意のフィールド名を埋め込みます。

サンプルコード。

github.com


バリデーション用のアノテーションは以下の通り。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Constraint(validatedBy = { SampleValidator.class })
public @interface SampleValidation {
    String value();
    String message() default "{com.example.demo.constraint.SampleValidation.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

今回は Controller で @Validated を利用して Form バリデーションをするパターン。

@RestController
public class DemoController {
    @Autowired
    MessageSource messageSource;
    @PostMapping("index")
    public String index(@RequestBody @Validated FooForm form, Errors errors) {
        if (errors.hasErrors()) {
            errors.getAllErrors().stream()
                    .map(e -> messageSource.getMessage(e, Locale.getDefault()))
                    .forEach(System.out::println);
            return "NG";
        }
        return "OK";
    }
    static class FooForm {
        @SampleValidation("foo")
        private String name; // バリデーション対象のフィールド
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
}

パターン1

プロパティ未定義の場合、Form クラスのフィールド名がメッセージ定義の {0} にそのまま出力される。

com.example.demo.constraint.SampleValidation.message={0} is invalid.

出力結果:

> name is invalid.

パターン2

フィールド名をキーにしてプロパティを定義した場合、定義した名前がメッセージ定義の {0} に埋め込まれて出力される。<Bean 名>.<フィールド名> 形式も可。

com.example.demo.constraint.SampleValidation.message={0} is invalid.
name=Name

出力結果:

> Name is invalid.

パターン3

<フィールド名> 形式と <Bean 名>.<フィールド名> 形式を一緒に定義した場合、<Bean 名>.<フィールド名> 形式のプロパティが優先される。

com.example.demo.constraint.SampleValidation.message={0} is invalid.
name=Name
fooForm.name=NAME

出力結果:

> NAME is invalid.

現場からは以上です。