如何使用(Java)Akka HTTP基于Accept-header返回XML?

时间:2017-02-10 11:19:29

标签: java xml marshalling akka-http

我使用Akka HTTP( Java 版本)来创建REST API' s。我有一个返回application / json的工作概念证明。不幸的是,我找不到任何明确的文档和/或(工作)示例,如何使它也返回其他东西(在我的例子中:text / xml)。

这是我目前所拥有的相关位(只要我发送没有Accept标头或Accept header application / json的请求,就会返回Airport对象的JSON表示):

return route(
  get(() -> 
    pathPrefix("reference", () -> 
      pathPrefix("v1", () -> 
        pathPrefix("airport", () -> 
          parameter("iataCode", (String iataCode) -> {
              final CompletionStage<Optional<Airport>> futureMaybeAirport = fetchAirport(iataCode);
              return onSuccess(
                () -> futureMaybeAirport
                , maybeAirport -> maybeAirport.map(airport -> 
                    completeOK(airport, Jackson.marshaller())
                  ).orElseGet(() -> 
                    complete(StatusCodes.NOT_FOUND, "Not Found")
                  )
              );
            }
          )
        )
      )
    )
  )
);

当我在Accept头中放置text / xml时,我得到了一个HTTP 406.所以,经过一些研究后,似乎我必须提供自己的Marshaller才能输出text / xml。知道Akka使用Jackson我想使用提供XMLMapper的jackson-dataformat-xml模块。所以,我按照以下方式创建我的自定义Marshaller(这就是Akka在Jackson课程中的表现):

public static <T> Marshaller<T, RequestEntity> xmlMarshaller() {
    return Marshaller.wrapEntity(
            u -> toXML(u),
            Marshaller.stringToEntity(),
            MediaTypes.TEXT_XML
    );
}

private static String toXML(Object object) {
    try {
        return new XmlMapper().writeValueAsString(object);
    } catch (JsonProcessingException e) {
        throw new IllegalArgumentException("Cannot marshal to XML: " + object, e);
    }
}

到目前为止一切顺利,但现在是棘手的部分:我如何向Akka HTTP明确表示我现在希望它使用默认的JSON Marshaller或这个XML Marshaller?据我所知,你可以使用Marshaller.oneOf来做到这一点。不幸的是,在网上没有一个实际的例子可以找到oneOf(当然也不是在Akka HTTP的关于编组的Java文档中,这只是Scala版本的一个副本,并附有文档需要的说明固定)。

我想我需要做这样的事情:

List<Marshaller<Airport, RequestEntity>> marshallers = new ArrayList();
marshallers.add(Jackson.marshaller());
marshallers.add(xmlMarshaller());

Marshaller<Airport, RequestEntity> airportMarshaller = Marshaller.oneOf(JavaConversions.asScalaBuffer(marshallers).toSeq());

这样我就可以将我的completeOk改编为:

completeOK(airport, airportMarshaller)

不幸的是我无法让Marshaller.oneOf工作,编译器一直在抱怨:

incompatible types: inference variable A has incompatible equality constraints Object,Airport where A,B are type-variables:
A extends Object declared in method <A,B>oneOf(Seq<Marshaller<A,B>>)
B extends Object declared in method <A,B>oneOf(Seq<Marshaller<A,B>>)

我不清楚究竟是什么问题,我的A和B(Airport和RequestEntity)显然扩展了Object(就像Java中的几乎所有东西一样)。所以任何一双新眼睛的帮助都会受到赞赏!

另外:我在此假设Akka HTTP的内置内容协商将根据Accept标头中的值确定要使用哪个Marshaller。我在这个假设中是正确的还是我试图以完全错误的方式解决问题?

1 个答案:

答案 0 :(得分:2)

好的,所以我自己想出来了(新的一天,一双新鲜的眼睛; - )。

其实基本上已经在我的问题中写了:

 List<Marshaller<List<Airport>, RequestEntity>> marshallers = new ArrayList();
 marshallers.add(Jackson.marshaller());
 marshallers.add(xmlMarshaller());

 Marshaller<List<Airport>, RequestEntity> airportMarshaller = Marshaller.oneOf(JavaConversions.asScalaBuffer(marshallers).toSeq());

在完整的OK中,它实际上是这样的:

completeOK(airport, airportMarshaller);

xmlMarshaller()和toXML(Object)方法未更改。

所以,事实证明我是在正确的轨道,但只是忘记了我返回List&lt; Airport&gt;的事实。而不是导致编译器错误的机场。