akka 如何在Scala中使用Tapir创建具有多个模式的端点

mw3dktmi  于 2022-11-05  发布在  Scala
关注(0)|答案(1)|浏览(134)

我只是在尝试创建具有多个几何体形状的端点时遇到了一个问题。
我的模型如下所示:

sealed trait FileExampleTrait {
  def kind: String
}

case class FileExampleOne(name: String, length: Int) extends FileExampleTrait {
  override def kind: String = “one”
}

case class FileExampleTwo(name: String) extends FileExampleTrait {
  override def kind: String = “two”
}

case class FileExampleResponse(message: String)

我正在尝试创建这个端点:

val fileExample = baseEndpoint.post
    .in(“example”)
    .in(jsonBody[FileExampleTrait])
    .out(jsonBody[FileExampleResponse])
    .summary(“something”)
    .description(“something”)

端点的实作如下所示:

private val fileExample = toAkkaRoute(jwtConsumer, errorHandler)(
    FileApi.fileExample, { (scope: RequestScope, input: (FileExampleTrait)) =>
      print(scope)
      input match {
        case FileExampleOne(name, _) => Future.successful(FileExampleResponse(name).asRight)
        case FileExampleTwo(name) => Future.successful(FileExampleResponse(name).asRight)
      }
    }
  )

这只是我尝试创建的一个示例。我添加了基于此的模式派生:

val sOne = Schema.derived[FileExampleOne]
  val sTwo = Schema.derived[FileExampleTwo]
  implicit val sExampleTrait: Schema[FileExampleTrait] =
    Schema.oneOfUsingField[FileExampleTrait, String](_.kind, _.toString)(“one” -> sOne, “two” -> sTwo)

我创建了一个测试,用于尝试基于Akka HTTP的端点:

test(“Example test”) {
    new Fixture() {
      val request = FileExampleOne(“name”, 1)
      Post(s”/api/v1/files/example”, jsonEntity(request)).withHeaders(requestHeaders) ~> wrappedRoute ~> check {
        response should be(successful)
        contentType shouldEqual ContentTypes.`application/json`
      }
    }
  }

我得到的错误如下:

Response error: {“code”:400,“message”:“Invalid value for: body (No constructor for type FileExampleTrait, JObject(List((name,JString(name)), (length,JInt(1)))))“}

我一直在看这份文件。

0tdrvxhp

0tdrvxhp1#

这是因为trait本身没有构造函数,我想我明白你的意思了,你想把body解析成trait的子类,假设你有这样的类型/类层次结构:

T // parent trait
       / \
     C1  C2  // class 1, etc...
    /
  C3

现在您想将一些JSON反序列化为trait T,您需要定义自定义行为,如“首先尝试转换为C3,如果失败,尝试转换为C2,如果再次失败,尝试转换为C1”,然后您将获得T值。现在,根据您使用的JSON库,实现可能会有所不同。请参阅softwaremill的documentation以获得有关如何在Tapir中处理JSON的更多信息,如果您使用Play Json,我可以推荐:

object FileExampleOne {
  implicit val reader: Reads[FileExampleOne] = Json.reads
}

object FileExampleTwo {
  implicit val reader: Reads[FileExampleTwo] = Json.reads
}

object FileExampleTrait {
  implicit val reads: Reads[FileExampleTrait] = json => {
    json.validate[FileExampleOne] orElse json.validate[FileExampleTwo]
  }
}

你可以看到它在scastie上运行。基于tapir documentations,你需要一个Codec来支持你的类型,创建JSON代码的方法之一是使用tapir支持的库之一(circe,Play Json,...)。

相关问题