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

How to return XML based on Accept-header using (Java) Akka HTTP?

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

这是我目前所拥有的相关部分(return是机场object的JSON-representation,只要我发送一个没有接受的请求header 或接受 header application/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")
                  )
              );
            }
          )
        )
      )
    )
  )
);

当我将 text/xml 放入 Accept header 时,我得到一个 HTTP 406。因此,经过一些研究,我似乎必须提供自己的编组器才能输出 text/xml。知道 Akka 使用 Jackson,我想使用提供 XMLMapper 的 jackson-dataformat-xml 模块。因此,我按如下方式创建自定义 Marshaller(Akka 在他们的 Jackson class 中也是这样做的):

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 编组器或这个 XML 编组器?据我了解,您可以使用 Marshaller.oneOf 来完成此操作。不幸的是,在网上找不到一个关于 oneOf 的实际例子(当然在 Akka HTTP 的 Java 关于编组的文档中也找不到,它只是 Scala-version 的副本,并附有文档需要的注释待修复)。

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

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 的 built-in 内容协商将根据 Accept header 中的值确定使用哪个编组器。我的这个假设是正确的还是我试图以完全错误的方式解决问题?

好的,所以我自己弄明白了(新的一天,一双新的眼睛;-)。

实际上我的问题中已经写了很多:

 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());

在 completeOK 中,它实际上是这样的:

completeOK(airport, airportMarshaller);

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

所以,事实证明我走在了正确的轨道上,但只是忘记了我返回的是 List 而不是导致编译器错误的机场这一事实。