Play JSON 组合器验证至少指定了一个字段
Play JSON combinator validates at least a field is specified
我有一个场景,当将 json 解析为 class 这样的案例时
implicit val userRead: Reads[User] = (
(__ \ "name").read[String] ~
(__ \ "email").readNullable[String] ~
(__ \ "phone").readNullable[String] ~
Reads.pure(None)
)(User.apply _)
我不要求 email
和 phone
都可用,但至少必须有一个可用。
在我的例子class定义中,我可以用
来防止它们都为空的情况
case class User(name: String, email: Option[String], phone: Option[String], id: Option[Long] = None) {
require(email.nonEmpty || phone.nonEmpty, "must have at least an email or phone number")
}
但是,这样做会产生异常,并响应 500 状态,而这应该是由于用户输入而导致的 400 错误。
我当然可以在我的控制器中手动执行验证,但我想知道是否有更简洁的方法来执行此操作。
我只能建议您手动编写自己的"reads"
case class A(i: Option[Int], j: Option[Int])
implicit val reads: Reads[A] = new Reads[A] {
override def reads(json: JsValue): JsResult[A] = {
(for {
i <- (json \ "i").validateOpt[Int]
j <- (json \ "j").validateOpt[Int]
} yield A(i, j))
.filter(JsError("both values can't be empty"))(a ⇒ a.j.nonEmpty || a.i.nonEmpty)
}
}
并进行测试:
scala> Json.parse("""{ "i" : 1} """).validate[A]
res4: play.api.libs.json.JsResult[A] = JsSuccess(A(Some(1),None),)
scala> Json.parse("""{} """).validate[A]
res5: play.api.libs.json.JsResult[A] = JsError(List((,List(ValidationError(List(from and to in range can't be both empty),WrappedArray())))))
您只需对现有代码进行少量添加即可实现此目的 (.filter(ValidationError("must have at least an email or phone number"))(u => u.email.isDefined || u.phone.isDefined)
):
case class User(name: String, email: Option[String], phone: Option[String], id: Option[Long] = None)
implicit val userRead: Reads[User] = (
(__ \ "name").read[String] ~
(__ \ "email").readNullable[String] ~
(__ \ "phone").readNullable[String] ~
Reads.pure(None)
)(User.apply _).filter(ValidationError("must have at least an email or phone number"))(u => u.email.isDefined || u.phone.isDefined)
现在,证明这是可行的:
scala> Json.parse("""{ "name" : "foobar"} """).validate[User]
res3: play.api.libs.json.JsResult[User] = JsError(List((,List(ValidationError(List(must have at least an email or phone number),WrappedArray())))))
scala> Json.parse("""{ "name" : "foobar", "email":"test"} """).validate[User]
res4: play.api.libs.json.JsResult[User] = JsSuccess(User(foobar,Some(test),None,None),)
scala> Json.parse("""{ "name" : "foobar", "phone":"test"} """).validate[User]
res5: play.api.libs.json.JsResult[User] = JsSuccess(User(foobar,None,Some(test),None),)
我有一个场景,当将 json 解析为 class 这样的案例时
implicit val userRead: Reads[User] = (
(__ \ "name").read[String] ~
(__ \ "email").readNullable[String] ~
(__ \ "phone").readNullable[String] ~
Reads.pure(None)
)(User.apply _)
我不要求 email
和 phone
都可用,但至少必须有一个可用。
在我的例子class定义中,我可以用
来防止它们都为空的情况case class User(name: String, email: Option[String], phone: Option[String], id: Option[Long] = None) {
require(email.nonEmpty || phone.nonEmpty, "must have at least an email or phone number")
}
但是,这样做会产生异常,并响应 500 状态,而这应该是由于用户输入而导致的 400 错误。
我当然可以在我的控制器中手动执行验证,但我想知道是否有更简洁的方法来执行此操作。
我只能建议您手动编写自己的"reads"
case class A(i: Option[Int], j: Option[Int])
implicit val reads: Reads[A] = new Reads[A] {
override def reads(json: JsValue): JsResult[A] = {
(for {
i <- (json \ "i").validateOpt[Int]
j <- (json \ "j").validateOpt[Int]
} yield A(i, j))
.filter(JsError("both values can't be empty"))(a ⇒ a.j.nonEmpty || a.i.nonEmpty)
}
}
并进行测试:
scala> Json.parse("""{ "i" : 1} """).validate[A]
res4: play.api.libs.json.JsResult[A] = JsSuccess(A(Some(1),None),)
scala> Json.parse("""{} """).validate[A]
res5: play.api.libs.json.JsResult[A] = JsError(List((,List(ValidationError(List(from and to in range can't be both empty),WrappedArray())))))
您只需对现有代码进行少量添加即可实现此目的 (.filter(ValidationError("must have at least an email or phone number"))(u => u.email.isDefined || u.phone.isDefined)
):
case class User(name: String, email: Option[String], phone: Option[String], id: Option[Long] = None)
implicit val userRead: Reads[User] = (
(__ \ "name").read[String] ~
(__ \ "email").readNullable[String] ~
(__ \ "phone").readNullable[String] ~
Reads.pure(None)
)(User.apply _).filter(ValidationError("must have at least an email or phone number"))(u => u.email.isDefined || u.phone.isDefined)
现在,证明这是可行的:
scala> Json.parse("""{ "name" : "foobar"} """).validate[User]
res3: play.api.libs.json.JsResult[User] = JsError(List((,List(ValidationError(List(must have at least an email or phone number),WrappedArray())))))
scala> Json.parse("""{ "name" : "foobar", "email":"test"} """).validate[User]
res4: play.api.libs.json.JsResult[User] = JsSuccess(User(foobar,Some(test),None,None),)
scala> Json.parse("""{ "name" : "foobar", "phone":"test"} """).validate[User]
res5: play.api.libs.json.JsResult[User] = JsSuccess(User(foobar,None,Some(test),None),)