I’m making a real push to understand the async powers of Play but finding a lot of conflict with regard to places where async invocation fits and places where the framework seems to conspire against its use.
The example I have relates to form validation. Play allows for ad-hoc constraints to be defined – see this from the docs:
val loginForm = Form(
tuple(
"email" -> email,
"password" -> text
) verifying("Invalid user name or password", fields => fields match {
case (e, p) => User.authenticate(e,p).isDefined
})
)
Nice and clean. However, if I’m using a fully async data access layer (e.g. ReactiveMongo), such a call to User.authenticate(...) would return a Future and I’m thus in the dark as to how I can utilise the power of both the built in form binding features and the async tools.
It’s all well and good to publicise the async approach but I’m getting frustrated that certain parts of the framework don’t play so well with it. If the validation has to be done synchronously, it seems to defeat the point of the async approach. I’ve come across a similar problem when using Action composition – e.g. a security related Action that would make a call to ReactiveMongo.
Can anyone shed any light on where my comprehension is falling short?
Yes, validation in Play is designed synchronously. I think it’s because assumed that most of time there is no I/O in form validation: field values are just checked for size, length, matching against regexp, etc.
Validation is built over
play.api.data.validation.Constraintthat store function from validated value toValidationResult(eitherValidorInvalid, there is no place to putFuturehere).verifyingjust adds another constraint with user-defined function.So I think Data Binding in Play just isn’t designed for doing I/O while validation. Making it asynchronous would make it more complex and harder to use, so it kept simple. Making every piece of code in framework to work on data wrapped in
Futures is overkill.If you need to use validation with ReactiveMongo, you can use
Await.result. ReactiveMongo returns Futures everywhere, and you can block until completion of these Futures to get result insideverifyingfunction. Yes, it will waste a thread while MongoDB query runs.Maybe there’s way to not waste thread by using continuations, not tried it.
I think it’s good to discuss this in Play mailing list, maybe many people want to do asynchronous I/O in Play data binding (for example, for checking values against database), so someone may implement it for future versions of Play.