akka http 使用 Json 支持和 xmlsupport

akka http to use Json Support and xmlsupport

我想在部门为 "hr" 时以 xml 格式打印数据,但在部门为 "tech" 时以 json 格式打印数据。

我们可以使用 喷-json一起支持https://doc.akka.io/docs/akka-http/current/common/json-support.html and XML support https://doc.akka.io/docs/akka-http/current/common/xml-support.html

 private[rest] def route =
  (pathPrefix("employee") & get) {
    path(Segment) { id =>
      parameters('department ? "") { (flag) =>
        extractUri { uri =>
          complete {
            flag match {
              case "hr": =>  {

            HttpEntity(MediaTypes.`application/xml`.withCharset(HttpCharsets.`UTF-8`),"hr department")
            }
              case "tech" =>{
                HttpEntity(ContentType(MediaTypes.`application/json`), mapper.writeValueAsString("tech department"))

              }

            }
          }
        }
      }
    }
  }

我试过的解决方法 我通过使用 JsonProtocols 和 ScalaXmlSupport 尝试了以下内容,我得到了预期的 ToResponseMarshallable 编译错误,但找到了 Department

case class department(name:String)
private[rest] def route =
  (pathPrefix("employee") & get) {
    path(Segment) { id =>
      parameters('department ? "") { (flag) =>
        extractUri { uri =>
          complete {
            flag match {
              case "hr": =>  {

            complete(department(name =flag))
            }
              case "tech" =>{
                complete(department(name =flag))

              }

            }
          }
        }
      }
    }
  }

我认为你必须克服几个问题才能实现你想要的:

  1. 您想根据请求参数自定义响应类型。这意味着基于 implicit 的标准封送处理对您不起作用,您必须执行一些明确的步骤

  2. 您想将一些业务对象编组到 XML 字符串中。不幸的是,ScalaXmlSupport that you referenced does not support this, it can marshal only an XML-tree into a response. So you'll need some library that can do XML serialization. One option would be to use jackson-dataformat-xml with jackson-module-scala。这也意味着您必须编写自己的自定义 Marshaller。还好没那么难

下面是一些可能适合您的简单代码:

import akka.http.scaladsl.marshalling.{ToResponseMarshallable, Marshaller}

// json marshalling
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import spray.json._
import spray.json.DefaultJsonProtocol._
implicit val departmentFormat = DefaultJsonProtocol.jsonFormat1(department)
val departmentJsonMarshaller = SprayJsonSupport.sprayJsonMarshaller[department]

// xml marshalling, need to write a custom Marshaller
// there are several MediaTypes for XML such as `application/xml` and `text/xml`, you'll have to choose the one you need.
val departmentXmlMarshaller = Marshaller.StringMarshaller.wrap(MediaTypes.`application/xml`)((d: department) => {
  import com.fasterxml.jackson.dataformat.xml.XmlMapper
  import com.fasterxml.jackson.module.scala.DefaultScalaModule
  val mapper = new XmlMapper()
  mapper.registerModule(DefaultScalaModule)
  mapper.writeValueAsString(d)
})


private val route =
  (pathPrefix("employee") & get) {
    path(Segment) { id =>
      parameters('department ? "") { (flag) =>
        extractUri { uri => {
          flag match {
            case "hr" => {
              // explicitly use the XML marshaller 
              complete(ToResponseMarshallable(department(name = flag))(departmentXmlMarshaller))
            }
            case "tech" => {
              // explicitly use the JSON marshaller 
              complete(ToResponseMarshallable(department(name = flag))(departmentJsonMarshaller))
            }
          }
        }
        }
      }
    }
  }

请注意,为了使 Jackson XML 序列化程序正常工作,department class 应该是顶级 class 否则您将收到有关 bad root 的神秘错误名字.

Akka Http 已经内置了内容类型协商。理想情况下,您应该通过拥有一个知道如何将您的部门变成 xml 或 json 并让客户设置 Accept header.[=15 的编组器来使用它=]

然而,这听起来可能无法让您的客户这样做,所以您可以这样做,假设您已经为 xml 和 [=20] 制作了 ToEntityMarshaller[department] =] 使用 ScalaXmlSupportSprayJsonSupport.

val toXmlEntityMarshaller: ToEntityMarshaller[department] = ???
val toJsonEntityMarshaller: ToEntityMarshaller[department] = ???
implicit val departmentMarshaller = Marshaller.oneOf(toJsonEntityMarshaller, toXmlEntityMarshaller)

def route =
  parameters("department") { departmentName =>
    // capture the Accept header in case the client did request one
    optionalHeaderValueByType[Accept] { maybeAcceptHeader => 
      mapRequest ( _
        .removeHeader(Accept.name)
        // set the Accept header based on the department
        .addHeader(maybeAcceptHeader.getOrElse(
          Accept(departmentName match {
            case "hr" ⇒ MediaTypes.`application/xml`
            case "tech" ⇒ MediaTypes.`application/json`
          })
        ))
      ) (
        // none of our logic code is concerned with the response type
        complete(department(departmentName))
        )
    }
  }