在 akka http 中正确解组特征
properly unmarshalling trait in akka http
我已经阅读了文档和很多关于 SO 的问题,仍然无法弄清楚我做错了什么。
我从香草 akka http g8 模板开始,但我想使用特征而不是案例 class 来表示 POST 请求中的对象。
当使用 case classes 时,我的应用程序一切正常,但是当我尝试实现特征时,它失败了:
[error] CollectionRoutes.scala:38: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[collector.CollectorConfig]
[error] entity(as[CollectorConfig]) { config =>
[error] ^
[error] CollectionRoutes.scala:55: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[collector.CollectorStep]
[error] entity(as[CollectorStep]) { collstep =>
[error] ^
[error] CollectionRoutes.scala:77: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[collector.CollectorConfig]
[error] entity(as[CollectorConfig]) { config =>
[error] ^
[error] three errors found
这是我用来定义 json 格式的特征:
trait JsonSupport extends DefaultJsonProtocol {
import spray.json._
implicit val collectionTypeConverter = new EnumJsonConverter(CollectionType)
implicit val actionPerformedJsonFormat = jsonFormat1(ActionPerformed)
implicit val restCollectorStepFormat = jsonFormat3(RestCollectorStep)
implicit object collectorStepFormat extends RootJsonFormat[CollectorStep] {
def write(a: CollectorStep) = a match {
case p: RestCollectorStep => p.toJson
}
def read(value: JsValue) =
value.asJsObject.fields("collectionType") match {
case JsString("Rest") => value.convertTo[RestCollectorStep]
}
}
implicit val restCollectorConfigFormat = jsonFormat5(RestCollectorConfig)
implicit object collectorConfigFormat extends RootJsonFormat[CollectorConfig] {
def write(a: CollectorConfig) = a match {
case p: RestCollectorConfig => p.toJson
}
def read(value: JsValue) =
value.asJsObject.fields("collectionType") match {
case JsString("Rest") => value.convertTo[RestCollectorConfig]
}
}
}
这是定义我的路线的特征:
trait CollectionRoutes extends JsonSupport {
import spray.json._
implicit def system: ActorSystem
lazy val log = Logging(system, classOf[CollectionRoutes])
def collectorRegistryActor: ActorRef
implicit lazy val timeout = Timeout(5.seconds) // usually we'd obtain the timeout from the system's configuration
val settings = CorsSettings.defaultSettings.withAllowGenericHttpRequests(true)
lazy val collectionRoutes: Route = cors(settings) {
pathPrefix("createcollector") {
concat(
pathEnd {
concat(
post {
entity(as[CollectorConfig]) { config =>
val collectorCreated: Future[CollectorRegistryActor.ActionPerformed] =
(collectorRegistryActor ? MakeCollector(config)).mapTo[CollectorRegistryActor.ActionPerformed]
onSuccess(collectorCreated) { created =>
complete(created.description)
}
}
}
)
}
)
} ~
pathPrefix("reststep") {
concat(
pathEnd {
concat(
post {
entity(as[CollectorStep]) { collstep =>
val executionResult: Future[StepActor.StepResponse] = (collectorRegistryActor ? ExecuteStep(collstep)).mapTo[StepActor.StepResponse]
onSuccess(executionResult) { res =>
res match {
case StepActor.StepResult(step, response, result) => {
val res = (response :: result :: Nil).toJson.toString
complete(res)
}
case StepActor.StepError(step, error) => complete(error)
}
}
}
}
)
}
)
} ~
pathPrefix("restcollect") {
concat(
pathEnd {
concat(
post {
entity(as[CollectorConfig]) { config =>
val executionResult: Future[CollectorActor.CollectionResult] = (collectorRegistryActor ? ExecuteCollection(config)).mapTo[CollectorActor.CollectionResult]
onSuccess(executionResult) { response =>
complete(response.result)
}
}
}
)
}
)
}
}
}
这就是该特征的使用方式。
object CollectionServer extends App with CollectionRoutes {
implicit val system: ActorSystem = ActorSystem("CollectionServer")
implicit val materializer: ActorMaterializer = ActorMaterializer()
val collectorRegistryActor: ActorRef = system.actorOf(CollectorRegistryActor.props, "collectorRegistryActor")
lazy val routes: Route = collectionRoutes
Http().bindAndHandle(routes, "localhost", 9100)
println(s"Server online at http://localhost:9100/")
Await.result(system.whenTerminated, Duration.Inf)
}
我很困惑,因为我相信我已经以非常标准的方式设置了所有内容,但它并没有采用隐含的方式。
任何想法表示赞赏 - 谢谢。
想通了:
我应该扩展 SprayJsonSupport 而不是 DefaultJsonProtocol
trait JsonSupport extends SprayJsonSupport {
我已经阅读了文档和很多关于 SO 的问题,仍然无法弄清楚我做错了什么。
我从香草 akka http g8 模板开始,但我想使用特征而不是案例 class 来表示 POST 请求中的对象。
当使用 case classes 时,我的应用程序一切正常,但是当我尝试实现特征时,它失败了:
[error] CollectionRoutes.scala:38: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[collector.CollectorConfig]
[error] entity(as[CollectorConfig]) { config =>
[error] ^
[error] CollectionRoutes.scala:55: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[collector.CollectorStep]
[error] entity(as[CollectorStep]) { collstep =>
[error] ^
[error] CollectionRoutes.scala:77: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[collector.CollectorConfig]
[error] entity(as[CollectorConfig]) { config =>
[error] ^
[error] three errors found
这是我用来定义 json 格式的特征:
trait JsonSupport extends DefaultJsonProtocol {
import spray.json._
implicit val collectionTypeConverter = new EnumJsonConverter(CollectionType)
implicit val actionPerformedJsonFormat = jsonFormat1(ActionPerformed)
implicit val restCollectorStepFormat = jsonFormat3(RestCollectorStep)
implicit object collectorStepFormat extends RootJsonFormat[CollectorStep] {
def write(a: CollectorStep) = a match {
case p: RestCollectorStep => p.toJson
}
def read(value: JsValue) =
value.asJsObject.fields("collectionType") match {
case JsString("Rest") => value.convertTo[RestCollectorStep]
}
}
implicit val restCollectorConfigFormat = jsonFormat5(RestCollectorConfig)
implicit object collectorConfigFormat extends RootJsonFormat[CollectorConfig] {
def write(a: CollectorConfig) = a match {
case p: RestCollectorConfig => p.toJson
}
def read(value: JsValue) =
value.asJsObject.fields("collectionType") match {
case JsString("Rest") => value.convertTo[RestCollectorConfig]
}
}
}
这是定义我的路线的特征:
trait CollectionRoutes extends JsonSupport {
import spray.json._
implicit def system: ActorSystem
lazy val log = Logging(system, classOf[CollectionRoutes])
def collectorRegistryActor: ActorRef
implicit lazy val timeout = Timeout(5.seconds) // usually we'd obtain the timeout from the system's configuration
val settings = CorsSettings.defaultSettings.withAllowGenericHttpRequests(true)
lazy val collectionRoutes: Route = cors(settings) {
pathPrefix("createcollector") {
concat(
pathEnd {
concat(
post {
entity(as[CollectorConfig]) { config =>
val collectorCreated: Future[CollectorRegistryActor.ActionPerformed] =
(collectorRegistryActor ? MakeCollector(config)).mapTo[CollectorRegistryActor.ActionPerformed]
onSuccess(collectorCreated) { created =>
complete(created.description)
}
}
}
)
}
)
} ~
pathPrefix("reststep") {
concat(
pathEnd {
concat(
post {
entity(as[CollectorStep]) { collstep =>
val executionResult: Future[StepActor.StepResponse] = (collectorRegistryActor ? ExecuteStep(collstep)).mapTo[StepActor.StepResponse]
onSuccess(executionResult) { res =>
res match {
case StepActor.StepResult(step, response, result) => {
val res = (response :: result :: Nil).toJson.toString
complete(res)
}
case StepActor.StepError(step, error) => complete(error)
}
}
}
}
)
}
)
} ~
pathPrefix("restcollect") {
concat(
pathEnd {
concat(
post {
entity(as[CollectorConfig]) { config =>
val executionResult: Future[CollectorActor.CollectionResult] = (collectorRegistryActor ? ExecuteCollection(config)).mapTo[CollectorActor.CollectionResult]
onSuccess(executionResult) { response =>
complete(response.result)
}
}
}
)
}
)
}
}
}
这就是该特征的使用方式。
object CollectionServer extends App with CollectionRoutes {
implicit val system: ActorSystem = ActorSystem("CollectionServer")
implicit val materializer: ActorMaterializer = ActorMaterializer()
val collectorRegistryActor: ActorRef = system.actorOf(CollectorRegistryActor.props, "collectorRegistryActor")
lazy val routes: Route = collectionRoutes
Http().bindAndHandle(routes, "localhost", 9100)
println(s"Server online at http://localhost:9100/")
Await.result(system.whenTerminated, Duration.Inf)
}
我很困惑,因为我相信我已经以非常标准的方式设置了所有内容,但它并没有采用隐含的方式。
任何想法表示赞赏 - 谢谢。
想通了:
我应该扩展 SprayJsonSupport 而不是 DefaultJsonProtocol
trait JsonSupport extends SprayJsonSupport {