如何在Mashaller中使用http请求标头进行内容协商?

时间:2017-07-04 05:02:18

标签: json scala marshalling unmarshalling akka-http

我的应用支持protobuf和JSON序列化。对于JSON序列化,我使用com.trueaccord.scalapb.json.JsonFormat,我的dtos是从proto定义生成的。

com.trueaccord序列化程序将选项类型包装到JSON对象,这会导致某些客户端出现问题,因此我希望能够在不制动现有客户端的情况下支持org.json4s

我希望能够根据名为JFORMAT的自定义http标头选择一个序列化程序。我的想法是,如果发送此标头,我将使用json4s,否则我将使用trueaccord序列化器。

我设法创建了一个Unmarshaller,它可以根据标头值选择一个请求序列化器:

Unmarshaller.withMaterializer[HttpRequest, T](_ => implicit mat => {
  case request: HttpRequest =>
    val entity = request.entity
    entity.dataBytes.runFold(ByteString.empty)(_ ++ _).map(data => {
      entity.contentType match {
        case `applicationJsonContentType` =>
          val jsFormat = {
            val header = request.headers.find(h => h.name() == jsonFormatHeaderName)
            if (header.isEmpty) "1.0" else header.get.value()
          }

          val charBuffer = Unmarshaller.bestUnmarshallingCharsetFor(entity)
          val jsonText = data.decodeString(charBuffer.nioCharset().name())
          val dto = if(jsFormat == "2.0") {
            write[T](value)(formats) // New Formatter
          } else {
            JsonFormat.fromJsonString[T](jsonText) // Old Formatter
          }
          dto
        case `protobufContentType` =>
          companion.parseFrom(CodedInputStream.newInstance(data.asByteBuffer)) // Proto Formatter
        case _ =>
          throw UnsupportedContentTypeException(applicationJsonContentType, protobufContentType)
      }
    })

我想对我使用Marshaller.oneOf和JSON处理的Marshaller做同样的事情:

  Marshaller.withFixedContentType(contentType) { value =>
    val jsonText = JsonSerializer.toJsonString[T](value)
    HttpEntity(contentType, jsonText)
  }

有没有办法构建一个知道请求http头的Mashaller? Akka HTTP文档没有任何示例,我无法理解PredefinedToRequestMarshallers。

我是否需要以某种方式组合多个marshallers,或者我可以在请求序列化期间将一些元数据附加到上下文中,我可以稍后在Marshaller中使用它?如果可能,我想避免将meta附加到我的dto或使用自定义内容类型,如application/vnd.api+json

当我将响应格式化为Accept-Encoding时,我可以从请求中使用许多其他有用的信息,自定义标头(如唯一请求ID)来创建相关ID,我可以通过阅读{{1}来添加JSONP支持} query parmeter等。

澄清:我需要一个解决方案来使用Mashaller,它的子类或由工厂方法创建的自定义版本,或者可能是多个Marshallers链接在一起。 callback已使用Marshaller.withFixedContentType标题,因此必须有办法。我添加了额外奖励来奖励特定挑战的解决方案。我是黑客和变通办法,我问了这个问题,因为我需要一个解决特定方案的干净解决方案。

1 个答案:

答案 0 :(得分:0)

Custom Marshallers部分提到Marshaller.oneOf重载方法,这似乎是您想要的:

  

帮助创建一个超级编组者"来自一些   "子marshallers&#34 ;.内容协商确定哪个   "子编组"最终得到了这份工作。

Marshaller个随播广告对象有很多接收Seq[HttpHeader]的方法。您也可以查看他们的实现。

我没有时间自己查看源代码,但如果这还不足以让您走上正确的道路,请告诉我。

修改

怎么样?

get {
  optionalHeaderValueByName("JFORMAT") { format =>
    complete {
      format match {
        case Some(f) => "Complete with json4s"
        case _ => "Complete with trueaccord"
      }
    }
  }
}