Есть ли общий подход к обработке запросов PATCH в REST API с использованием библиотеки circe? По умолчанию 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)
}
Есть ли лучший способ обрабатывать запросы PATCH (с полями, допускающими значение NULL) вместо написания пользовательского кодека, который возвращается к no value
, если поле не указано в полезной нагрузке JSON? Спасибо