How is Akka HTTP marshaling implemented under the hood?

こ雲淡風輕ζ 提交于 2019-12-13 16:23:26

问题


The following Scala code compiles:

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)

with the following output in the worksheet:

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>

But when I comment out the import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ I get the following compilation error:

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

How does that import have this affect?


回答1:


When you do RequestContext#complete(response), which takes ToResponseMarshallable as input.

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 is the object where implicit Marshallers are defined and they are the ones which gives 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)

}

If you don't import SprayJsonSupport, you won't get implicit Marshallers which marshall your case class to desired output which is JSObject.

If you don't want to import SprayJsonSupport that provides default toJsonMarshallers, write your own, or copy paste the marshallers from JsonSpraySupport.

example

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

}

tests

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"}"""
      }
    }
  }
}


来源:https://stackoverflow.com/questions/45068547/how-is-akka-http-marshaling-implemented-under-the-hood

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!