[2.3-scala] How to parse JSON Validation errors

154 views
Skip to first unread message

Ben Developer

unread,
Sep 11, 2014, 12:55:22 PM9/11/14
to play-fr...@googlegroups.com
Say I have code like this:

    case class userForm(username: String, password: String)


   
implicit val userFormReads: Reads[userForm] = (
     
(JsPath \ "username").read[String](email) and
     
(JsPath \ "password").read[String](minLength[String](6))
   
)(userForm.apply _)


    val userFormResult
: JsResult[userForm] = request.body.validate[userForm](userFormReads)


    userFormResult match
{
     
case e: JsError =>
       
new JsonResult("error", Messages(e.errors(0)._2(0).message)).send
     
case s: JsSuccess[userForm] =>
 
// process form  .....


If I get something like "error.email", everything works great because in my messages.en-US I have defined error.email=Please enter a valid email address.
However if the 2nd validation fails (the one on password minlength 6), then I get: Minimum length is {0} as the result

What's the best way to handle validation errors in a humanly readable format? I need to pass something like this to the frontend to show our standard error message:
{"errors": true, message: "Please enter a valid password that is longer than 6 characters"} 

Basically...it would be great to force a certain error message if any of the validations fail. Something like this would be awesome:

implicit val userFormReads: Reads[userForm] = (
      
(JsPath \ "username").read[String](email).getOrElse("Please enter a valid Username") and
      
(JsPath \ "password").read[String](minLength[String](6)).getOrElse("Please enter a valid Password")
    
)(userForm.apply _)

Is this possible?


Thanks!

Ryan Tanner

unread,
Sep 11, 2014, 2:00:33 PM9/11/14
to play-fr...@googlegroups.com
This works but could probably be a little cleaner:

implicit class ReadsWithError[T](reads: Reads[T]) {
 
def orError(msg: String): Reads[T] = {
    reads
.orElse(new Reads[T] { def reads(json: JsValue) = JsError(Seq((__, Seq(ValidationError(msg))))) })
 
}

}

implicit val userFormReads: Reads[userForm] = (

 
(__ \ "username").read[String](email.orError("Please enter a valid username")) and
 
(__ \ "password").read[String](minLength[String](6).orError("Please enter a valid password"))
)(userForm.apply _)

And then...

scala> val testObj = Json.obj("username" -> "blah", "password" -> "123")
testObj
: play.api.libs.json.JsObject = {"username":"blah","password":"123"}

scala
> testObj.validate(userFormReads)
res2: play.api.libs.json.JsResult[userForm] = JsError(List((/username,List(ValidationError(Please enter a valid username,WrappedArray()))), (/password,List(ValidationError(Please enter a valid password,WrappedArray())))))


Ben Developer

unread,
Sep 11, 2014, 3:03:20 PM9/11/14
to play-fr...@googlegroups.com
Thats awesome -- works perfectly! Can't believe there's no built-in functionality for custom error messages.

Ryan Tanner

unread,
Sep 11, 2014, 3:42:05 PM9/11/14
to play-fr...@googlegroups.com
There sort of is through .filter/.filterNot, which let you provide your own ValidationError.  This method is a little weird since it uses .orElse to purposefully fail when it's meant to be used for recovery.

Ben Developer

unread,
Sep 11, 2014, 3:57:01 PM9/11/14
to play-fr...@googlegroups.com
Interesting.


Your way seems to be cleaner and I like it way more. What I've done is created  a trait called CustomController which extends Controller, included your implicit definition of orElse, then changed all of my controllers to extend CustomController, that way I don't have to repeat those lines for each controller....and it's working great

Thanks so much. Hope a cleaner approach makes its way into a future Play release.
Reply all
Reply to author
Forward
0 new messages