【发布时间】:2020-05-10 16:51:50
【问题描述】:
是否有使用 circe 库处理 REST API 中的 PATCH 请求的通用方法?默认情况下,circe 不允许解码仅指定部分字段的部分 JSON,即它需要设置所有字段。您可以使用withDefaults 配置,但无法知道您收到的字段是null 还是未指定。这是可能解决方案的简化示例。它使用Left[Unit] 作为值来处理根本没有指定字段的情况:
# possible payloads
{
"firstName": "Foo",
"lastName": "Bar"
}
{
"firstName": "Foo"
}
{
"firstName": null
}
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport._
import io.circe.generic.auto._
import io.circe.{Decoder, HCursor}
case class User(firstName: Option[String], lastName: String)
// In PATCH request only 1 field can be specified. The rest could be omitted. Left represents `not specified`
case class PatchUserRequest(firstName: Either[Unit, Option[String]], lastName: Either[Unit, String])
object PatchUserRequest {
implicit val decode: Decoder[PatchUserRequest] = new Decoder[PatchUserRequest] {
final def apply(c: HCursor): Decoder.Result[PatchUserRequest] =
for {
// Here we handle `no field specified` error cases as Left[Unit]
foo <- c.downField("firstName").as[Option[String]] match {
case Left(noFieldSpecified) => Right(Left(()))
case Right(result) => Right(Right(result))
}
bar <- c.downField("lastName").as[String] match {
case Left(noFieldSpecified) => Right(Left(()))
case Right(result) => Right(Right(result))
}
} yield PatchUserRequest(foo, bar)
}
}
object Apis extends Directives {
var user = User("Foo", "Bar")
val create = path("user")(post(entity(as[User])(newUser => user = newUser)))
val patch = path("user")(patch(entity(as[PatchUserRequest])(patchRequest => patch(patchRequest))))
// If field is specified - update the record, ignore otherwise
def patch(request: PatchUserRequest) {
request.firstName.foreach(newFirstName => user = user.copy(firstName = newFirstName)
request.lastName.foreach(newlastName => user = user.copy(lastName = newlastName)
}
如果 JSON 有效负载中未指定字段,是否有更好的方法来处理 PATCH 请求(带有可为空的字段)而不是编写回退到 no value 的自定义编解码器?谢谢
【问题讨论】: