如何在引擎盖下实施Akka HTTP编组?

时间:2017-07-12 22:29:48

标签: scala akka-http

以下Scala代码编译:

import spray.json.DefaultJsonProtocol._
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives.complete

case class Item(name: String, id: Long)

implicit val itemFormat = jsonFormat2(Item)

val item = Item("xbox", 123)

complete(item)

在工作表中使用以下输出:

import spray.json.DefaultJsonProtocol._
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives.complete

defined class Item

itemFormat: spray.json.RootJsonFormat[Item] = spray.json.ProductFormatsInstances$$anon$2@4528e00

item: Item = Item(xbox,123)

res0: akka.http.scaladsl.server.StandardRoute = <function1>

但是当我注释掉import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._时,我收到以下编译错误:

Error:(11, 11) type mismatch;
 found   : A$A562.this.Item
 required: akka.http.scaladsl.marshalling.ToResponseMarshallable
complete(item);}
         ^

导入有何影响?

1 个答案:

答案 0 :(得分:2)

执行RequestContext#complete(response)时,ToResponseMarshallable作为输入。

package akka.http.scaladsl.server

@InternalApi
private[http] class RequestContextImpl(

  override def complete(trm: ToResponseMarshallable): Future[RouteResult] =
    trm(request)(executionContext)
      .fast.map(res ⇒ RouteResult.Complete(res))(executionContext)
      .fast.recover {
        case Marshal.UnacceptableResponseContentTypeException(supported) ⇒
          RouteResult.Rejected(UnacceptedResponseContentTypeRejection(supported) :: Nil)
        case RejectionError(rej) ⇒
          RouteResult.Rejected(rej :: Nil)
      }(executionContext)

}

SprayJsonSupport是定义隐式Marshallers的对象,它们是Marshallable

的对象
package akka.http.scaladsl.marshallers.sprayjson

trait SprayJsonSupport {

  implicit def sprayJsonMarshallerConverter[T](writer: RootJsonWriter[T])(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[T] =
    sprayJsonMarshaller[T](writer, printer)
  implicit def sprayJsonMarshaller[T](implicit writer: RootJsonWriter[T], printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[T] =
    sprayJsValueMarshaller compose writer.write
  implicit def sprayJsValueMarshaller(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[JsValue] =
    Marshaller.StringMarshaller.wrap(MediaTypes.`application/json`)(printer)

}

如果您没有导入SprayJsonSupport,则无法隐式Marshallers将您的案例类编组为所需的输出JSObject

如果您不想导入提供默认为JsonMarshallers的SprayJsonSupport,请编写您自己的,或复制粘贴来自JsonSpraySupport的marshallers。

例如

object GetHttpRoutes {

  case class Acknowledge(status: String)
  implicit val itemFormat = jsonFormat1(Acknowledge)

  implicit def toJsonMarshallerConverter[Entity](writer: RootJsonWriter[Entity])(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[Entity] =
    toJsonMarshaller[Entity](writer, printer)

  implicit def toJsonMarshaller[Entity](implicit writer: RootJsonWriter[Entity], printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[Entity] =
    toJsValueMarshaller compose writer.write

  implicit def toJsValueMarshaller(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[JsValue] =
    Marshaller.StringMarshaller.wrap(MediaTypes.`application/json`)(printer)

  val get_api =
      path("") {
        get { context =>
          context.complete {
            Acknowledge(status = "xbox")
          }
        }
      }
}

trait HTTPRoutes {

  implicit val system: ActorSystem
  implicit val materializer: ActorMaterializer

  val route = GetHttpRoutes.get_api

}

<强>测试

class GetHttpRoutesCompSpecs extends WordSpec with Matchers with ScalatestRouteTest with BeforeAndAfterAll {

  "HTTP GET endpoints" should {

    "returns xbox on /" in {
      Get("/") ~> GetHttpRoutes.get_api ~> check {
        responseAs[String] shouldEqual """{"status":"xbox"}"""
      }
    }
  }
}