Spring の検証インターフェースを使用した検証
Spring は、オブジェクトの検証に使用できる Validator
インターフェースを備えています。Validator
インターフェースは Errors
オブジェクトを使用して機能するため、検証中にバリデーターは Errors
オブジェクトに検証エラーを報告できます。
次の小さなデータオブジェクトの例を考えてみましょう。
Java
Kotlin
public class Person {
private String name;
private int age;
// the usual getters and setters...
}
class Person(val name: String, val age: Int)
次の例では、org.springframework.validation.Validator
インターフェースの以下の 2 つのメソッドを実装することにより、Person
クラスの検証動作を提供します。
supports(Class)
: このValidator
は、提供されたClass
のインスタンスを検証できますか?validate(Object, org.springframework.validation.Errors)
: 指定されたオブジェクトを検証し、検証エラーの場合、指定されたErrors
オブジェクトに登録します。
Spring Framework が提供する ValidationUtils
ヘルパークラスを知っている場合は特に、Validator
の実装は非常に簡単です。次の例では、Person
インスタンスに Validator
を実装しています。
Java
Kotlin
public class PersonValidator implements Validator {
/**
* This Validator validates only Person instances
*/
public boolean supports(Class clazz) {
return Person.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
Person p = (Person) obj;
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old");
}
}
}
class PersonValidator : Validator {
/**
* This Validator validates only Person instances
*/
override fun supports(clazz: Class<*>): Boolean {
return Person::class.java == clazz
}
override fun validate(obj: Any, e: Errors) {
ValidationUtils.rejectIfEmpty(e, "name", "name.empty")
val p = obj as Person
if (p.age < 0) {
e.rejectValue("age", "negativevalue")
} else if (p.age > 110) {
e.rejectValue("age", "too.darn.old")
}
}
}
ValidationUtils
クラスの static
rejectIfEmpty(..)
メソッドは、null
または空の文字列である場合、name
プロパティを拒否するために使用されます。ValidationUtils
javadoc を見て、前に示した例以外にどのような機能が提供されているかを確認してください。
リッチオブジェクトの各ネストされたオブジェクトを検証するために単一の Validator
クラスを実装することは確かに可能ですが、独自の Validator
実装でオブジェクトの各ネストされたクラスの検証ロジックをカプセル化する方が良い場合があります。「リッチ」オブジェクトの簡単な例は、2 つの String
プロパティ(1 番目と 2 番目の名前)と複雑な Address
オブジェクトで構成される Customer
です。Address
オブジェクトは Customer
オブジェクトとは独立して使用できるため、別個の AddressValidator
が実装されています。CustomerValidator
で AddressValidator
クラスに含まれるロジックをコピーアンドペーストに頼らずに再利用したい場合、次の例に示すように、CustomerValidator
内で AddressValidator
を依存性注入またはインスタンス化できます。
Java
Kotlin
public class CustomerValidator implements Validator {
private final Validator addressValidator;
public CustomerValidator(Validator addressValidator) {
if (addressValidator == null) {
throw new IllegalArgumentException("The supplied [Validator] is " +
"required and must not be null.");
}
if (!addressValidator.supports(Address.class)) {
throw new IllegalArgumentException("The supplied [Validator] must " +
"support the validation of [Address] instances.");
}
this.addressValidator = addressValidator;
}
/**
* This Validator validates Customer instances, and any subclasses of Customer too
*/
public boolean supports(Class clazz) {
return Customer.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
Customer customer = (Customer) target;
try {
errors.pushNestedPath("address");
ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
} finally {
errors.popNestedPath();
}
}
}
class CustomerValidator(private val addressValidator: Validator) : Validator {
init {
if (addressValidator == null) {
throw IllegalArgumentException("The supplied [Validator] is required and must not be null.")
}
if (!addressValidator.supports(Address::class.java)) {
throw IllegalArgumentException("The supplied [Validator] must support the validation of [Address] instances.")
}
}
/*
* This Validator validates Customer instances, and any subclasses of Customer too
*/
override fun supports(clazz: Class<>): Boolean {
return Customer::class.java.isAssignableFrom(clazz)
}
override fun validate(target: Any, errors: Errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required")
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required")
val customer = target as Customer
try {
errors.pushNestedPath("address")
ValidationUtils.invokeValidator(this.addressValidator, customer.address, errors)
} finally {
errors.popNestedPath()
}
}
}
検証エラーは、バリデーターに渡される Errors
オブジェクトに報告されます。Spring Web MVC の場合、<spring:bind/>
タグを使用してエラーメッセージをインスペクションできますが、Errors
オブジェクトを自分でインスペクションすることもできます。提供するメソッドの詳細については、javadoc を参照してください。
バリデータは、バインディングプロセスを伴わずに、特定のオブジェクトの即時検証のためにローカルに呼び出されることもあります。6.1 では、これが新しい Validator.validateObject(Object)
メソッドによって簡略化され、デフォルトで使用できるようになりました。このメソッドは、インスペクション可能な単純な Errors
表現を返します。通常は、エラーサマリーメッセージを例外 (たとえば、validator.validateObject(myObject).failOnError(IllegalArgumentException::new)
) に変換するには、hasErrors()
または新しい failOnError
メソッドを呼び出します。