0
votes

I'm trying to get Play to validate + convert a json request body into a case class. If the validation fails, I'd like to send back a list of human readable error messages.

My case class is a simple login request:

case class LoginReq(user: String, password: String)

I'd like to send errors in a simple format, such as:

{"errors": ["User was not provided", "Password was not provided"]}

This is what I've implemented so far:

@Singleton
class LoginController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {

    case class LoginReq(user: String, password: String)
    implicit val reqFormat = Json.format[LoginReq]


  def index() = Action(parse.json) { req =>

          val loginReq = req.body.validate[LoginReq]
          loginReq match {
              case JsSuccess(r: LoginReq, path: JsPath) => Ok(Json.toJson(r))
              case e: JsError =>
                  UnprocessableEntity(JsError.toJson(e))
          }
  }
}

The problem is, the line :

case e: JsError => UnprocessableEntity(JsError.toJson(e))

produces the errors in this format:

{
    "obj.user": [
        {
            "msg": [
                "error.path.missing"
            ],
            "args": []
        }
    ],
    "obj.password": [
        {
            "msg": [
                "error.path.missing"
            ],
            "args": []
        }
    ]
}

rather than:

{"errors": ["User was not provided", "Password was not provided"]}

Is there a built in / easy way that i'm missing, to convert the errors into a more human readable way? At least convert error.path.missing into something like was not provided? Or do I need to write this myself?

1

1 Answers

0
votes

Something along these lines should work ...

[edit]

def index = Action(parse.json) { request =>
    val loginResult = request.body.validate[LoginRequest]
    loginResult.fold(
      errors => {
        BadRequest(Json.obj("status" ->"KO", "message" -> errors.map {
          case x if x._1.path.nonEmpty ⇒ s"Invalid ${x._1.toString().stripPrefix("/")}"
        }))
      },
      login => {
        Ok(Json.obj("status" ->"OK", "message" -> s"Login successful as ${login.user}" ))
      }
    )
  }