使用 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 Framework 還提供了 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))。