如何使用 (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 而不是导致编译器错误的机场这一事实。
我正在使用 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