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

Jen*_*zlo 10 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)
      }
    })
Run Code Online (Sandbox Code Playgroud)

我想要做同样与我的Marshaller我与Marshaller.oneOf和JSON处理一个看起来像使用:

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

有没有办法构建一个知道请求http标头的Mashaller?在阿卡HTTP文档没有任何例子,我不能让PredefinedToRequestMarshallers感.

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

当我将响应格式化为Accept-Encoding,自定义标头(如唯一请求ID)以创建相关ID时,我可以从请求中使用许多其他有用的信息,我可以通过读取callback查询参数来添加JSONP支持等.

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

Abh*_*kar 0

自定义编组器部分提到了Marshaller.oneOf重载方法,这似乎就是您想要的:

用于从多个“子编组器”创建“超级编组器”的帮助器。内容协商决定了最终由哪个“子编组器”来完成这项工作。

Marshaller伴随对象有许多接收Seq[HttpHeader]. 您也可以研究他们的实现。

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

编辑

怎么样?

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