【问题标题】:Custom JSON validation constraints in Play Framework 2.3 (Scala)Play Framework 2.3 (Scala) 中的自定义 JSON 验证约束
【发布时间】:2014-12-06 16:07:36
【问题描述】:

我设法使用自定义约束实现表单验证,但现在我想对 JSON 数据做同样的事情。

如何将自定义验证规则应用于 JSON 解析器?

示例:客户端的 POST 请求中包含一个用户名(username),我不仅要确保该参数为非空文本,还要确保该用户确实存在于数据库中。

// 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 _)
}

据我所知,但这只能确保username 是一个字符串。 如何应用我的附加自定义验证规则,例如检查给定的用户是否真的存在?这甚至可能吗?

当然,我可以在操作中的case success 部分中使用我的obj 并在那里执行额外的检查,但这似乎不太优雅,因为那样我就必须创建自己的错误消息并且可以在某些情况下只有用户JsError.toFlatJson(error)。经过几个小时的搜索和尝试,我找不到任何示例。

对于常规形式,我会使用如下内容:

// 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!")
    }
)

但是如果数据以 JSON 格式提交呢?

【问题讨论】:

  • 我建议在 JSON 验证后验证您的用户。我认为最好使用 JSON 验证来确保您可以进入 Scala 对象领域,然后您需要做的所有其他事情都会变得更加舒适。

标签: json scala validation playframework-2.3 playframework-json


【解决方案1】:

我能想到的最简单的方法是使用Reads 中的filter 方法。

假设我们有一些User 对象来确定用户名是否存在:

object User {
    def findByName(name: String): Option[User] = ...
}

然后你可以像这样构造你的Reads

import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.data.validation._

case class ExampleCaseClass(username: String, somethingElse: String)

object ExampleCaseClass {
    implicit val exampleReads: Reads[ExampleCaseClass] = (
        (JsPath \ "username").read[String].filter(ValidationError("User does not exist."))(findByName(_).isDefined) and
        (JsPath \ "somethingElse").read[String]
    )(ExampleCaseClass.apply _)
}

您的控制器功能可以使用 json BodyParserfold 来简化:

def postNew = Action(parse.json) { implicit request =>
    request.body.validate[ExampleCaseClass].fold(
        error => BadRequest(JsError.toFlatJson(error)),
        obj => {
            // Do something with the validated object..
        }
    )
}

您还可以创建一个单独的Reads[String] 来检查用户是否存在,并在您的Reads[ExampleCaseClass] 中明确使用该Reads[String]

val userValidate = Reads.StringReads.filter(ValidationError("User does not exist."))(findByName(_).isDefined)

implicit val exampleReads: Reads[ExampleCaseClass] = (
    (JsPath \ "username").read[String](userValidate) and
    (JsPath \ "somethingElse").read[String]
)(ExampleCaseClass.apply _)

【讨论】:

  • m-z 问题是,如果您使用许多这样的验证器,如果前一个验证器失败,后续验证器仍将运行。看到这个,例如:stackoverflow.com/questions/31077937/…
  • @FelipeAlmeida 一般来说,最好累积错误,而不是停留在第一个错误上(这就是以这种方式构建库的原因)。这就是为什么我个人不会使用像这样进行数据库调用的 JSON 验证器,而是在其他地方处理它。但这与这个问题无关。
  • 我相信如果你的 findByName 返回 Future 或 DBIO (slick) 这很可能是行不通的。播放框架的验证 API 并非有意支持 Futures。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-17
  • 2015-02-11
  • 1970-01-01
  • 1970-01-01
  • 2015-12-29
  • 1970-01-01
  • 2015-04-29
相关资源
最近更新 更多