はい、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 の Data Binding は、検証中に I/O を実行するようには設計されていないと思います。非同期にすると複雑で使いにくくなるので、シンプルに保ちました。フレームワーク内のすべてのコードをFuture
s でラップされたデータで動作させるのはやり過ぎです。
ReactiveMongo で検証を使用する必要がある場合は、Await.result
. ReactiveMongo はどこでも Future を返します。これらの Future が完了するまでブロックして、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 を実行したいと考えているので (たとえば、データベースに対して値をチェックするため)、誰かが Play の将来のバージョンでそれを実装するかもしれません。