Validation by Using Spring’s Validator Interface
Spring 提供了一个 Validator
接口,您可以使用它来验证对象。Validator
接口通过使用 Errors
对象工作,因此在验证过程中,验证器可以将验证失败报告给 Errors
对象。
Spring features a Validator
interface that you can use to validate objects. The
Validator
interface works by using an Errors
object so that, while validating,
validators can report validation failures to the Errors
object.
请考虑以下小型数据对象的示例:
Consider the following example of a small data object:
-
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
接口方法为 Person
类提供验证行为:
The next example provides validation behavior for the Person
class by implementing the
following two methods of the org.springframework.validation.Validator
interface:
-
supports(Class)
: Can thisValidator
validate instances of the suppliedClass
? -
validate(Object, org.springframework.validation.Errors)
: Validates the given object and, in case of validation errors, registers those with the givenErrors
object.
实现 Validator
相当简单,特别是如果你知道 Spring Framework 也提供的 ValidationUtils
帮助类。以下示例为 Person
实例实现 Validator
:
Implementing a Validator
is fairly straightforward, especially when you know of the
ValidationUtils
helper class that the Spring Framework also provides. The following
example implements Validator
for Person
instances:
-
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(..)`方法用于拒绝 `name`属性(如果它是 `null`或空字符串)。看看 `ValidationUtils
javadoc,看看除了之前显示的示例之外它还提供了哪些功能。
The static
rejectIfEmpty(..)
method on the ValidationUtils
class is used to
reject the name
property if it is null
or the empty string. Have a look at the
ValidationUtils
javadoc
to see what functionality it provides besides the example shown previously.
尽管肯定可以实现一个 Validator
类来验证复合对象中的每个嵌套对象,但最好将每个嵌套对象的验证逻辑封装在它自己的 Validator
实现中。“复合”
对象的简单示例将是一个 Customer
,它由两个 String
属性(名和姓)和一个复杂的 Address
对象组成。Address
对象可以独立于 Customer
对象使用,因此已经实现了不同的 AddressValidator
。如果希望 CustomerValidator
重用 AddressValidator
类中包含的逻辑而不使用复制粘贴,则你可以在 CustomerValidator
中依赖注入或实例化 AddressValidator
,如下例所示:
While it is certainly possible to implement a single Validator
class to validate each
of the nested objects in a rich object, it may be better to encapsulate the validation
logic for each nested class of object in its own Validator
implementation. A simple
example of a “rich” object would be a Customer
that is composed of two String
properties (a first and a second name) and a complex Address
object. Address
objects
may be used independently of Customer
objects, so a distinct AddressValidator
has been implemented. If you want your CustomerValidator
to reuse the logic contained
within the AddressValidator
class without resorting to copy-and-paste, you can
dependency-inject or instantiate an AddressValidator
within your CustomerValidator
,
as the following example shows:
-
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 中找到。
Validation errors are reported to the Errors
object passed to the validator. In the case
of Spring Web MVC, you can use the <spring:bind/>
tag to inspect the error messages, but
you can also inspect the Errors
object yourself. More information about the
methods it offers can be found in the javadoc.
还可以本地调用验证器来立即验证给定对象,而不需要绑定过程。从 6.1 开始,这已通过新的 Validator.validateObject(Object)
方法简化,该方法现在默认可用,返回一个简单的 Errors
表示,可以对其进行检查:通常调用 hasErrors()
或新方法 failOnError
将错误摘要消息转换为异常(例如 validator.validateObject(myObject).failOnError(IllegalArgumentException::new)
)。
Validators may also get locally invoked for the immediate validation of a given object,
not involving a binding process. As of 6.1, this has been simplified through a new
Validator.validateObject(Object)
method which is available by default now, returning
a simple ´Errors` representation which can be inspected: typically calling hasErrors()
or the new failOnError
method for turning the error summary message into an exception
(e.g. validator.validateObject(myObject).failOnError(IllegalArgumentException::new)
).