I managed to implement form validation with custom constraints, but now I want to do the same thing with JSON data.
How can I apply custom validation rules to a JSON parser?
Example: The client's POST request contains a user name (username
) and not only do I want to make sure that this parameter is a non-empty text, but also that this user actually exists in the database.
// In the controller...
def postNew = Action { implicit request =>
request.body.asJson.map { json =>
json.validate[ExampleCaseClass] match {
case success: JsSuccess[ExampleCaseClass] =>
val obj: ExampleCaseClass = success.get
// ...do something with obj...
Ok("ok")
case error: JsError =>
BadRequest(JsError.toFlatJson(error))
}
} getOrElse(BadRequest(Json.obj("msg" -> "JSON request expected")))
}
// In ExampleCaseClass.scala...
case class ExampleCaseClass(username: String, somethingElse: String)
object ExampleCaseClass {
// That's what I would use for a form:
val userCheck: Mapping[String] = nonEmptyText.verifying(userExistsConstraint)
implicit val exampleReads: Reads[ExampleCaseClass] = (
(JsPath \ "username").read[String] and
(JsPath \ "somethingElse").read[String]
)(ExampleCaseClass.apply _)
}
That's as far as I get, but this only ensures that username
is a String. How do I apply my additional custom validation rule, e.g. to check if the given user really exists? Is this even possible?
Sure, I could take my obj
in the case success
section in the action and perform additional checks there, but this doesn't seem very elegant, because then I'd have to create my own error message and could only user JsError.toFlatJson(error)
for some cases. After searching and trying for hours I couldn't find any examples.
For regular forms I'd use something like this:
// In the controller object...
val userValidConstraint: Constraint[String] = Constraint("constraints.uservalid")({ username =>
if (User.find(username).isDefined) {
Valid
} else {
val errors = Seq(ValidationError("User does not exist"))
Invalid(errors)
}
})
val userCheck: Mapping[String] = nonEmptyText.verifying(userValidConstraint)
val exampleForm = Form(
mapping(
"username" -> userCheck
// ...and maybe some more fields...
)(ExampleCaseClass.apply)(ExampleCaseClass.unapply)
)
// In the controller's action method...
exampleForm.bindFromRequest.fold(
formWithErrors => {
BadRequest("Example error message")
},
formData => {
// do something
Ok("Valid!")
}
)
But what if the data is submitted as JSON?