是的,Play中的验证是同步设计的。我认为这是因为假设大多数时候在表单验证中没有I/O:只检查字段值的大小,长度,与正则表达式的匹配等。
验证建立在play.api.data.validation.Constraint
上,验证函数从验证值到ValidationResult
(Valid
或Invalid
,这里没有地方可以放Future
)。
/**
* A form constraint.
*
* @tparam T type of values handled by this constraint
* @param name the constraint name, to be displayed to final user
* @param args the message arguments, to format the constraint name
* @param f the validation function
*/
case class Constraint[-T](name: Option[String], args: Seq[Any])(f: (T => ValidationResult)) {
/**
* Run the constraint validation.
*
* @param t the value to validate
* @return the validation result
*/
def apply(t: T): ValidationResult = f(t)
}
verifying
只是增加了用户定义函数的另一个约束。
所以我认为Play中的数据绑定并不是为了在验证时进行I/O而设计的。使其异步将使它更复杂,更难以使用,所以它保持简单。使框架中的每一段代码都适用于Future
中包装的数据是过度的。
如果您需要使用ReactiveMongo验证,则可以使用Await.result
。 ReactiveMongo无处不在地返回期货,并且您可以阻止这些期货完成以获得verifying
函数内的结果。是的,它会在MongoDB查询运行时浪费一个线程。
object Application extends Controller {
def checkUser(e:String, p:String):Boolean = {
// ... construct cursor, etc
val result = cursor.toList().map(_.length != 0)
Await.result(result, 5 seconds)
}
val loginForm = Form(
tuple(
"email" -> email,
"password" -> text
) verifying("Invalid user name or password", fields => fields match {
case (e, p) => checkUser(e, p)
})
)
def index = Action { implicit request =>
if (loginForm.bindFromRequest.hasErrors)
Ok("Invalid user name")
else
Ok("Login ok")
}
}
也许有办法不浪费线程通过使用continuations,没有尝试过。
我认为在Play邮件列表中讨论这个问题很好,也许很多人想要在Play数据绑定中执行异步I/O(例如,用于检查数据库的值),所以有人可能会为未来的版本玩。
如何设置验证信息动态地:
摘自?例如,消息可能是“无效的用户名或密码”或“现在服务不可用”。 第二个问题是我可以在没有重复认证请求的情况下获取User对象吗? – Artem 2014-07-28 07:41:05