使用 Spring 的 Validator 接口进行验证

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 接口的以下两种方法,为 Person 类提供验证行为

  • supports(Class):此 Validator 是否可以验证提供的 Class 的实例?

  • validate(Object, org.springframework.validation.Errors):验证给定的对象,并在出现验证错误的情况下,使用给定的 Errors 对象注册这些错误。

实现 Validator 非常简单,尤其是在您了解 Spring 框架也提供的 ValidationUtils 帮助类的情况下。以下示例实现了 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(..) 方法用于在 name 属性为 null 或空字符串时拒绝它。查看 ValidationUtils javadoc 以了解除了前面显示的示例之外它还提供了哪些功能。

虽然当然可以实现单个 Validator 类来验证丰富对象中每个嵌套对象的验证,但最好将每个嵌套对象的验证逻辑封装到其自己的 Validator 实现中。一个简单的“丰富”对象的示例是 Customer,它由两个 String 属性(第一个和第二个名称)和一个复杂的 Address 对象组成。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 表示形式,可以对其进行检查:通常调用 hasErrors() 或新的 failOnError 方法将错误摘要消息转换为异常(例如,validator.validateObject(myObject).failOnError(IllegalArgumentException::new))。