【问题标题】:Play2/Scala Custom JSON field validationPlay2/Scala 自定义 JSON 字段验证
【发布时间】:2015-04-30 13:10:00
【问题描述】:

假设我有这个模型,我在其中定义了一个具有特定 JSON 格式的自定义案例类 IPAddress 地址。此类包含 IPv4 的字符串表示形式,并在构造函数中进行验证,如果输入字符串无效,则会引发 IllegalArgumentException。

case class Node(id: UUID, name: String, ip: IPAddress)

case class IPAddress(s: String) {
  val rx = """^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$""".r
  require(rx.pattern.matcher(s).matches())

  val ip = s
  override def toString = ip
}


object JsonNodeFormat {
  import play.api.libs.json.Json

  implicit val midWrite: Writes[FMMid] = Writes {
    (mid: FMMid) => JsString(mid.toString)
  }
  implicit val midRead: Reads[FMMid] = JsPath.read[String].map(FMMid(_))

  implicit val NodeFormat = Json.format[Node]
}

然后我的控制器带有一个创建新节点并将其写入数据库的操作(在我的情况下,我使用的是 ReactiveMongo,但这无关紧要)

class Nodes extends Controller with MongoController {
  def collection: JSONCollection = db.collection[JSONCollection]("nodes")

  import models._
  import models.JsonNodeFormat._

  def createTest = Action.async(parse.json) {
    request =>
      request.body.validate[Node].map {
        node =>
          collection.insert(node).map {
            lastError =>
              Created(s"Node Created")
          }
      }.getOrElse(Future.successful(BadRequest("invalid json")))
  }
}

如果使用有效的 json 发出请求

{
    "id": "0879d4be-78bb-4cc0-810b965b",
    "ip": "192.168.0.10",
    "name": "node1"
}

一切正常。该对象已正确添加到数据库中。我希望控制器在传递无效 IP 地址时返回 BadRequest 响应。相反,如果我传递一个 IP 地址无效的 Json

{
    "id": "0879d4be-78bb-4cc0-810b965b",
    "ip": "192.168.0.foo",
    "name": "node1"
}

它会导致内部服务器错误,所有堆栈都打印在控制台上(因为没有人捕获构造函数异常)。我希望当传递一个无效的 IPAddress 时,request.body.validate 函数失败并且执行落在 getOrElse 语句中。

另请注意,传递无效的 UUID 不会生成错误,而是会生成 BadRequest 回复。

我的 IPAddress 类中缺少什么?

【问题讨论】:

  • 你有没有想过返回Try[String]而不是String?您可以包装 ip 验证,以便在遇到验证问题时做出反应。
  • 是的,这绝对是一个解决方案。但我想要求 body.validate 所有工作,这样我就不需要另一个代码块,因此不需要额外的嵌套级别。我只是想知道,因为在 UUID 中一切都按预期工作。
  • Play 让您可以选择如何回复特定异常。您可以在对象 Global 中覆盖函数 onError 的定义。在那里您可以获得异常,您将能够返回 BadRequest 代码。
  • 这就是我目前采用的解决方案。这种方法的缺点是您会丢失有关哪些特定验证失败的信息。
  • 在这种情况下,我会摆脱 require 并使用像 findFirstIn 这样的另一个函数。然后你可以处理 Option[String]

标签: json scala playframework-2.0


【解决方案1】:

最终我找到了解决方案。我发现在其他读取验证助手之间有一个名为pattern 的函数将字符串与正则表达式匹配。所以我改变了我的 Json 读取为

  implicit val midRead: Reads[FMMid] = JsPath.read[String](pattern("""^5\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$""".r)).map(FMMid(_))

一切都开始按我的预期工作。

附:如果没有任何自定义验证器,仍然无法理解 UUID 是如何工作的。

【讨论】:

  • Play 提供了一个Reads[UUID],这就是为什么您不需要创建自己的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-05
  • 2015-10-03
  • 2014-12-06
  • 2014-03-04
  • 2011-02-01
相关资源
最近更新 更多