使用Spring的Validator接口进行验证

Spring提供了一个Validator接口,您可以使用它来验证对象。Validator接口通过使用一个Errors对象来工作,因此在验证过程中,验证器可以向Errors对象报告验证失败。

考虑以下一个小数据对象的示例:

  • Java

  • Kotlin

public class Person {

	private String name;
	private int age;

	// 常规的getter和setter方法...
}
class Person(val name: String, val age: Int)

下一个示例为Person类提供验证行为,通过实现org.springframework.validation.Validator接口的以下两个方法:

  • supports(Class): 这个Validator能够验证提供的Class的实例吗?

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

实现一个Validator是非常简单的,特别是当您知道Spring Framework还提供的ValidationUtils辅助类时。以下示例为Person实例实现了Validator

  • Java

  • Kotlin

public class PersonValidator implements Validator {

	/**
	 * 此验证器仅验证Person实例
	 */
	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 {

	/**
	 * 此验证器仅验证Person实例
	 */
	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实现中。一个“丰富”对象的简单示例可能是一个由两个String属性(名字和姓氏)和一个复杂的Address对象组成的CustomerAddress对象可以独立于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("所提供的[Validator]是必需的,不得为null。");
		}
		if (!addressValidator.supports(Address.class)) {
			throw new IllegalArgumentException("所提供的[Validator]必须支持[Address]实例的验证。");
		}
		this.addressValidator = addressValidator;
	}

	/**
	 * 此验证器验证Customer实例,以及Customer的任何子类
	 */
	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("所提供的[Validator]是必需的,不得为null。")
		}
		if (!addressValidator.supports(Address::class.java)) {
			throw IllegalArgumentException("所提供的[Validator]必须支持[Address]实例的验证。")
		}
	}

	/*
	* 此验证器验证Customer实例,以及Customer的任何子类
	*/
	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))。