是的,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