我正在使用 scala 3.3 和 Akka actor 构建 Akka HTTP 应用程序。
当在
star, planet or moon
对象中创建 StarRegistry
时,我希望将其值临时存储在 registry
函数的映射变量 map: Map[String, Set[HeavenlyBody]]
中。
map
应存储如下所示的值,每个参数都有其自己的类型,如下所示:
"stars" -> Set[CreateStar],
"planets" -> Set[CreatePlanet],
"moons" -> Set[CreateMoon],
在
starRoutes
脚本中是 API。
stars
API 路由用于创建新的星星,并获取上面存储map
中存储的所有星星。
当获取
stars
GET API 路由时,我希望 api 从上面的 stars
函数的 stars
中获取存储在 registry
字段中的所有 map
。
star
功能的描述与moon
和planet
功能的描述类似。
当前代码给出错误:
Type Mismatch Error: D:\Studio\WORK\akka\AKKA-HTTP\scala-akka-http-interstellar-IoT\src\main\scala\com\example\StarRegistry.scala:
replyTo ! Stars(map.get("stars").toSeq)
Found: Seq[Set[com.example.HeavenlyBody]]
Required: Seq[com.example.CreateStar]
Type Mismatch Error: D:\Studio\WORK\akka\AKKA-HTTP\scala-akka-http-interstellar-IoT\src\main\scala\com\example\StarRegistry.scala:199:45
replyTo ! Planets(map.get("planets").toSeq)
Found: Seq[Set[com.example.HeavenlyBody]]
Required: Seq[com.example.CreatePlanet]
Type Mismatch Error: D:\Studio\WORK\akka\AKKA-HTTP\scala-akka-http-interstellar-IoT\src\main\scala\com\example\StarRegistry.scala:213:41
replyTo ! Moons(map.get("moons").toSeq)
Found: Seq[com.example.HeavenlyBody]
Required: Seq[com.example.CreateMoon]
我当前的代码如下所示。
预先感谢您的帮助!
StarRegistry.scala:
sealed trait HeavenlyBody
final case class CreateStar(id: String, name: String, location: String, size: Option[Size]) extends HeavenlyBody
final case class CreatePlanet(id: String, name: String, location: String, size: Option[Size]) extends HeavenlyBody
final case class CreateMoon(id: String, name: String, location: String, size: Option[Size]) extends HeavenlyBody
final case class Stars(stars: immutable.Seq[CreateStar])
final case class Planets(planets: immutable.Seq[CreatePlanet])
final case class Moons(moons: immutable.Seq[CreateMoon])
final case class Size(radius: Long)
object StarRegistry {
// actor protocol
sealed trait Command
final case class GetStars(replyTo: ActorRef[Stars]) extends Command
final case class NewStar(star: CreateStar, replyTo: ActorRef[ActionPerformed]) extends Command
final case class GetPlanets(replyTo: ActorRef[Planets]) extends Command
final case class NewPlanet(planet: CreatePlanet, replyTo: ActorRef[ActionPerformed]) extends Command
final case class GetMoons(replyTo: ActorRef[Moons]) extends Command
final case class NewMoon(moon: CreateMoon, replyTo: ActorRef[ActionPerformed]) extends Command
final case class ActionPerformed(description: String)
def apply(): Behavior[Command] = registry(Map("stars" -> Set.empty, "planets" -> Set.empty, "moons" -> Set.empty))
private def registry(map: Map[String, Set[HeavenlyBody]]): Behavior[Command] =
Behaviors.receiveMessage {
case GetStars(replyTo) =>
replyTo ! Stars(map.get("stars").toSeq)
Behaviors.same
case NewStar(star, replyTo) =>
replyTo ! ActionPerformed(s"Star '${star.name}' created!")
registry(map("stars") += star)
case GetPlanets(replyTo) =>
replyTo ! Planets(map.get("planets").toSeq)
Behaviors.same
case NewPlanet(planet, replyTo) =>
replyTo ! ActionPerformed(s"Planet '${planet.name}' created!")
registry(map("planets") += planet)
case GetMoons(replyTo) =>
replyTo ! Moons(map.get("moons").toSeq)
Behaviors.same
case NewMoon(moon, replyTo) =>
replyTo ! ActionPerformed(s"Moon '${moon.name}' created!")
registry(map("moons") += moon)
}
}
jsonFormats.scala:
...
...
object JsonFormats {
import DefaultJsonProtocol._
implicit val sizeJsonFormat: RootJsonFormat[Size] = jsonFormat1(Size.apply)
implicit val createStarJsonFormat: RootJsonFormat[CreateStar] = jsonFormat4(CreateStar.apply)
implicit val createPlanetJsonFormat: RootJsonFormat[CreatePlanet] = jsonFormat4(CreatePlanet.apply)
implicit val createMoonJsonFormat: RootJsonFormat[CreateMoon] = jsonFormat4(CreateMoon.apply)
implicit val starsJsonFormat: RootJsonFormat[Stars] = jsonFormat1(Stars.apply)
implicit val planetsJsonFormat: RootJsonFormat[Planets] = jsonFormat1(Planets.apply)
implicit val moonsJsonFormat: RootJsonFormat[Moons] = jsonFormat1(Moons.apply)
implicit val actionPerformedJsonFormat: RootJsonFormat[ActionPerformed] = jsonFormat1(ActionPerformed.apply)
}
starRoutes.scala:
class StarRoutes(starRegistry: ActorRef[StarRegistry.Command])(implicit val system: ActorSystem[_]) {
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import JsonFormats._ //#import-json-formats
def getStars(): Future[Stars] =
starRegistry.ask(GetStars.apply)
def createStar(star: CreateStar): Future[ActionPerformed] =
starRegistry.ask(NewStar(star, _))
def getPlanets(): Future[Planets] =
starRegistry.ask(GetPlanets.apply)
def createPlanet(planet: CreatePlanet): Future[ActionPerformed] =
starRegistry.ask(NewPlanet(planet, _))
def getMoons(): Future[Moons] =
starRegistry.ask(GetMoons.apply)
def getMoon(name: String): Future[GetMoonResponse] =
starRegistry.ask(GetMoon(name, _))
val starRoutes: Route =
...
pathPrefix("stars") {
concat(
pathEnd {
concat(
get {
complete(getStars())
},
post {
entity(as[CreateStar]) { star =>
onSuccess(createStar(star)) { performed =>
complete((StatusCodes.Created, performed))
}
}
})
},
}
...
pathPrefix("planets") {
concat(
pathEnd {
concat(
get {
complete(getPlanets())
},
post {
entity(as[CreatePlanet]) { planet =>
onSuccess(createPlanet(planet)) { performed =>
complete((StatusCodes.Created, performed))
}
}
})
},
...
pathPrefix("moons") {
concat(
pathEnd {
concat(
get {
complete(getMoons())
},
post {
entity(as[CreateMoon]) { moon =>
onSuccess(createMoon(moon)) { performed =>
complete((StatusCodes.Created, performed))
}
}
})
}
private def registry(map: Map[String, Set[HeavenlyBody]]): Behavior[Command] =
Behaviors.receiveMessage {
case GetStars(replyTo) =>
replyTo ! Stars(map.get("stars").toSeq)
您收到错误是因为
map.get(...).toSeq
返回 Seq[HeavenlyBody]
而您想要 Seq[CreateStar]
。您可能认为 Seq
的所有元素都是 CreateStar
,但编译器无法判断。
解决方案是使用
collect
来约束类型:
replyTo ! Stars(map.get("stars").toSeq.collect{x: CreateStar => x})
这将丢弃所有不是
CreateStar
的元素并根据需要返回 Seq[CreateStar]
。
您可能还想考虑使用
enum HeavenlyBody
而不是密封特征。