思路是这样,每个表单实现Validatable特质。这个特质要求实现返回一个属性名到验证器列表的映射。
表单验证器执行时首先获取表单所有的属性(除去class),然后遍历这个映射,运行字段对应的验证器变成响应的验证错误(如果有的话),否则最后是个空集合。
FormValidatorRunner(代码最下方)是测试类,执行结果是
Map(name -> List(ValidateError(default.notBlank,List())))
代码如下:
import scala.collection.Traversable
case class ValidateError(messageCode: String,
messageArguments: Traversable[Any] = List[Any]())
trait FieldRule {
def apply(form: Validatable, propertyValue: Option[Any]): Option[ValidateError]
}
object NotBlankFieldRule extends FieldRule {
def apply(form: Validatable, propertyValue: Option[Any]) = propertyValue match {
case Some(value) if value.toString.trim.length > 0 => None
case _ => Some(ValidateError("default.notBlank"))
}
}
trait Validatable {
def getRules(): Map[String, Traversable[FieldRule]]
}
class FormError(fieldErrors: Map[String, Traversable[ValidateError]]) {
def hasError(): Boolean = fieldErrors.isEmpty
override def toString = fieldErrors.toString
}
object FormValidator {
def apply(form: Validatable): FormError = {
val props = getProps(form)
new FormError(form.getRules.map{
case (fieldName, rules) => (fieldName,
rules.flatMap(_.apply(form, props.get(fieldName))))
})
}
private def getProps(obj: Any): Map[String, Any] = {
java.beans.Introspector.getBeanInfo(
obj.getClass).getPropertyDescriptors.flatMap{ d =>
Option(d.getReadMethod).map(getter => (d.getName, getter.invoke(obj)))
}.toMap - "class"
}
}
object FormValidatorRunner {
class Person(id: Long, name: String) extends Validatable {
def getId(): Long = id
def getName(): String = name
def getRules = Map("name" -> List(NotBlankFieldRule))
}
def main(args: Array[String]): Unit = {
println(FormValidator.apply(new Person(1L, "")))
}
}