使用 Akka Http 和 Circe 的端点、负载实体和案例类的默认值

问题描述

使用 Scala 实现 Rest API 服务我使用的是 Akka Http v10.2.3 和 Circe v0.13.0。

假设我有这个端点:


import io.circe.generic.auto._

...

pathPrefix("test") { 
    post {
      entity(as[SomeData]) { data =>
        // do something with the data (instance of SomeData)
        ...
        complete(StatusCodes.OK)
      }
    }
  }

和 SomeData 案例类:

case class SomeData(someString: String,someInt: Int = 0)

如果我用这个 json 到达这个端点:

{ "someString": "value","someInt": 123}

然后 Circe 会将 SomeData(value,123) 存储在变量 data 中。

如果我用这个 json 到达端点:

{ "someString": "value"}

然后 Circe 将失败,因为缺少 someInt

当有效负载为 {"someString: "value"} 且 json 中缺少 someInt 时(不使用 Option),Circe 是否可以将值 0 设置为案例类中的默认值?

更新: 我正在尝试使用 io.circe.generic.extras 但我无法让它工作(此时我在两个源中创建了一个隐式 val 但它也不起作用)这里是完整的源:

package com.defaultValues

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import scala.util.Success
import scala.util.Failure
import SomeData._

object SomeApi extends App with FailFastCirceSupport {
  val serviceName: String          = "default-values"
  implicit val system: ActorSystem = ActorSystem(serviceName)
  import system.dispatcher

  import io.circe.generic.extras.Configuration
  import io.circe.generic.extras.auto._
  implicit val someDataConfig: Configuration = Configuration.default.withDefaults

  val routes = pathPrefix("default-values") {
    post {
      entity(as[SomeData]) { data =>
        println(s"POST /test,body $data")
        complete(data)
      }
    }
  }

  Http()
    .newServerAt("localhost",8084)
    .bind(routes)
    .onComplete {
      case Success(_)         => println(s"app started $serviceName")
      case Failure(exception) => println(s"app failed to start $serviceName",exception)
    }
}
package com.defaultValues

import io.circe.{Decoder,Encoder}
import io.circe.generic.semiauto.{deriveDecoder,deriveEncoder}

case class SomeData(someString: String,someInt: Int = 0)

object SomeData {
  import io.circe.generic.extras.Configuration
  implicit val customConfig: Configuration = Configuration.default.withDefaults

  implicit val someDataDecoder: Decoder[SomeData] = deriveDecoder[SomeData]
  implicit val someDataEncoder: Encoder[SomeData] = deriveEncoder[SomeData]
}

build.sbt:

scalaVersion := "2.13.2"

val akkaVersion          = "2.6.9"
val akkaHttpVersion      = "10.2.0"
val akkaHttpCirceVersion = "1.31.0"
val circeVersion         = "0.13.0"


libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor-typed"     % akkaVersion,"com.typesafe.akka" %% "akka-cluster-typed"   % akkaVersion,"com.typesafe.akka" %% "akka-http"            % akkaHttpVersion,"de.heikoseeberger" %% "akka-http-circe"      % akkaHttpCirceVersion,"io.circe"          %% "circe-core"           % circeVersion,"io.circe"          %% "circe-generic"        % circeVersion,"io.circe"          %% "circe-parser"         % circeVersion,"io.circe"          %% "circe-refined"        % circeVersion,"io.circe"          %% "circe-generic-extras" % circeVersion,)

示例: 当我发送此有效负载时:

{ "someString": "value1" }

我明白了:

The request content was malformed:
Attempt to decode value on failed cursor: DownField(someInt)

解决方法

我找到了解决方案:

取而代之的是:

application_name.py

我不得不使用这个:

implicit val someDataDecoder: Decoder[SomeData] = deriveDecoder[SomeData]
implicit val someDataEncoder: Encoder[SomeData] = deriveEncoder[SomeData]

并添加此导入:

implicit val someDataDecoder: Decoder[SomeData] = deriveConfiguredDecoder
implicit val someDataEncoder: Encoder[SomeData] = deriveConfiguredEncoder

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...