Last active
August 30, 2016 08:53
-
-
Save jeroenr/8956587 to your computer and use it in GitHub Desktop.
Parameter validation with Play 2 framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object ApplicationBuild extends Build { | |
val appName = "my-app" | |
val appVersion = "1-SNAPSHOT" | |
val appDependencies = Seq( | |
jdbc, | |
cache | |
) | |
val main = play.Project(appName, appVersion, appDependencies) | |
.settings( | |
routesImport += "binders._", | |
) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package binders | |
import play.api.mvc.QueryStringBindable | |
import util.{Try, Success, Failure} | |
import services.ParamValidator | |
import play.api.data.validation._ | |
import play.api.i18n.Messages | |
case class Pager(offset: Int, size: Int) | |
object Pager { | |
val NUM = "num" | |
val SIZE = "size" | |
val DEFAULT_NUM = 1 | |
val DEFAULT_SIZE = 30 | |
val CONSTRAINTS = Seq(ParamValidator.MIN_0, Constraints.max(50000000)) | |
implicit def queryStringBinder(implicit intBinder: QueryStringBindable[Int]) = new QueryStringBindable[Pager] { | |
override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, Pager]] = { | |
val pagingKeys = Seq(s"$key.$NUM", s"$key.$SIZE") | |
val pagingParams = pagingKeys.filter(params.keys.toSeq.contains(_)) | |
val result = for { | |
num <- Try(intBinder.bind(pagingKeys(0), params).get).recover { | |
case e => Right(DEFAULT_NUM) | |
} | |
size <- Try(intBinder.bind(pagingKeys(1), params).get).recover { | |
case e => Right(DEFAULT_SIZE) | |
} | |
} yield { | |
(num.right.toOption, size.right.toOption) | |
} | |
result match { | |
case Success((maybeNum, maybeSize)) => | |
ParamValidator(CONSTRAINTS, maybeNum, maybeSize) match { | |
case Valid => | |
Some(Right(Pager(maybeNum.get - 1, maybeSize.get))) | |
case Invalid(errors) => | |
Some(Left(errors.zip(pagingParams).map { | |
case (ValidationError(message, v), param) => Messages(message, param, v) | |
}.mkString(", "))) | |
} | |
case Failure(e) => Some(Left(s"Invalid paging params: ${e.getMessage}")) | |
} | |
} | |
override def unbind(key: String, pager: Pager): String = { | |
intBinder.unbind(s"$key.$NUM", pager.offset + 1) + "&" + intBinder.unbind(s"$key.$SIZE", pager.size) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package services | |
import play.api.data.validation.{Constraint, Invalid, Valid, Constraints} | |
/** | |
* Created by jeroen on 2/7/14. | |
*/ | |
object ParamValidator { | |
val MIN_0 = Constraints.min(0) | |
def apply[T](constraints: Iterable[Constraint[T]], optionalParam: Option[T]*) = | |
optionalParam.flatMap { _.map { param => | |
constraints flatMap { | |
_(param) match { | |
case i:Invalid => Some(i) | |
case _ => None | |
} | |
} | |
} | |
}.flatten match { | |
case Nil => Valid | |
case invalids => invalids.reduceLeft { | |
(a,b) => a ++ b | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
GET /api/users controllers.UserController.list(page: Pager) |
Haha great :). I made a pull request for this stuff here playframework/playframework#2377. Would be nice if they merge it in in some way.
Added sample of using the validator in a custom query string binder
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hahaha I was looking back at the code right now and I had the same idea!