2017-07-04 97 views
9

我的应用程序支持protobuf和JSON序列化。对于JSON序列化,我使用com.trueaccord.scalapb.json.JsonFormat,我的dtos是从proto定义生成的。如何在Mashaller中使用http请求头进行内容协商?

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我与Marshaller.oneOf和JSON处理一个使用看起来像:

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

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

我需要多marshallers某种方式合并或者我可以请求序列我可以在以后的Marshaller使用过程中添加一些元数据的情况下?我想避免附加元到我的DTO如果可能的话,或使用自定义内容类型一样application/vnd.api+json

有很多其他有用的信息,当我格式化喜欢的Accept-Encoding响应我可以从使用请求,自定义标题一样独特请求ID创建一个相关ID,我可以通过读取callback查询parmeter添加JSONP支持等

澄清:我需要通过创建它的解决方案中使用的Mashaller,子类或自定义版本工厂方法或多个链接在一起的Marshailer。 Marshaller.withFixedContentType已经使用Accept标题,所以必须有一个方法。我添加了赏金以奖励针对特定挑战的解决方案。我是黑客和变通办法的软件,我问了这个问题,因为我需要一个干净的解决方案来解决特定的场景。

+0

我可能失去了一些东西:你为什么不实例化编组收到请求后,一旦你知道哪一个你需要什么? 解组所有内容类型的解组是有意义的,但编组几乎是将您的答案转换为任何您想要的,所以如果您希望它取决于请求,请将其作为请求的函数? – C4stor

回答

0

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

助手创建从一些 “子marshallers”“超级编组”。内容协商确定哪个 “子编组”最终能够完成这项工作。

Marshaller伴侣对象有很多方法可以收到Seq[HttpHeader]。你也可以看看他们的实现。

我没有时间寻找到的源代码自己,但如果这是不够的,让你在正确的道路上,让我知道。

编辑

怎么样?

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

我正在使用Marshaller.oneOf与多个Marshaller.withFixedContentType marshallers但这些只使用Accept标头。 WithOpenCharset和Opaque也没有引用请求上下文。 –

+0

我需要一个marsheller,它可以与oneOf一起使用,但可以根据自定义标题进行决策。 –

+0

@JenoLaszlo啊,你没有提到你的问题,你已经在使用'oneOf'编组。你使用什么类型的内容?而且,您是否考虑使用“Content-Type”标题来解决这个问题,而不是在自定义标题上进行回复? –