如何解决Spring WebFlux - 流式 XML 响应的内容类型
我成功地使用 application/x-ndjson
作为将返回的 Flux<>
格式化为以换行符分隔的独立 JSON 文档流的内容类型。
我寻求一种对 XML 执行相同操作的内容类型 - 将返回的 Flux<>
格式化为以换行符分隔的独立 XML 文档流。
我尝试了 application/stream+xml
和 application/xml-external-parsed-entity
,但没有成功。
当我尝试 application/xml
时,只有 Flux<>
的第一个元素被格式化;流的其余部分被忽略,结果只是一个 xml 文档。
如果 Spring WebFlux 中不存在预配置的内容类型,我希望能够提供有关如何使用 WebFluxConfigurer
来实现此类内容类型的指导。
我使用的是 Spring Boot 2.4.2 (Spring WebFlux 5.3.3),并且很乐意将这些依赖项升级到最新版本。
更新:
我已经升级到 Spring Boot 2.5.3 和 Spring WebFlux 5.3.9。
@GetMapping 配置为多种响应类型:
@GetMapping(value = "/allevents",produces = {
"application/stream+xml","application/x-ndjson","application/xml-external-parsed-entity","application/xml",})
当使用 -H 'accept: application/x-ndjson'
通过 curl 调用时,我会返回一个以换行符分隔的 JSON 文档流。因此该用例有效。
当使用 -H 'accept: application/stream+xml'
或 -H 'accept: application/xml'
通过 curl 调用时,我只返回一个 XML 文档,该文档仅表示 Flux 中的一个项目,可能是第一个。所以这个用例失败了,因为我需要将完整的流作为独立的 XML 文档返回。
当使用 -H 'accept: application/xml-external-parsed-entity'
通过 curl 调用时,我收到带有消息 "No Encoder for [...] with preset Content-Type 'null'
的 HTTP 500 响应
更新:以下是完整的 curl 命令:
curl -X 'GET' 'http://localhost:9030/allevents' -H 'accept: application/xml' -s
curl -X 'GET' 'http://localhost:9030/allevents' -H 'accept: application/x-ndjson' -s
curl -X 'GET' 'http://localhost:9030/allevents' -H 'accept: application/xml-external-parsed-entity' -s
curl -X 'GET' 'http://localhost:9030/allevents' -H 'accept: application/stream+xml' -s
更新:
这是两次 curl 调用的示例输出。 XML 表示中没有 eventOccurred 属性,因为它是 Instant,我还没有研究它。
> curl -X 'GET' 'http://localhost:9030/allevents' -H 'accept: application/stream+xml' -s -N
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><datasetEvent><entryId>1625247900924</entryId><eventOccurred/><setId>1625247900924</setId><setBusinessDate/><setName>file24.txt</setName><setSize>0</setSize><status>Processed</status><context>GAAPC</context><transform>true</transform><isReload>false</isReload></datasetEvent>
> curl -X 'GET' 'http://localhost:9030/allevents' -H 'accept: application/x-ndjson' -s | head
{"entryId":"1625247900924","eventOccurred":"2021-07-02T17:51:55.618Z","setId":"1625247900924","setBusinessDate":"2021-01-29","setName":"file24.txt","setSize":0,"status":"Processed","context":"GAAPC","transform":true,"isReload":false}
{"entryId":"1625220390247","eventOccurred":"2021-07-02T10:12:20.446Z","setId":"1625220390247","setBusinessDate":"2021-07-01","setName":"file02.txt","status":"Archived","isReload":false}
{"entryId":"1625159850563","eventOccurred":"2021-07-01T17:24:20.800Z","setId":"1625159850563","isReload":false}
{"entryId":"1624288800694","eventOccurred":"2021-06-21T15:28:45.546Z","setId":"1624288800694","setBusinessDate":"2021-06-01","setName":"file15.txt","context":"GAAPP","isReload":false}
{"entryId":"1624287720608","eventOccurred":"2021-06-21T15:09:36.169Z","setId":"1624287720608","isReload":false}
{"entryId":"1624281600449","eventOccurred":"2021-06-21T13:28:18.725Z","setId":"1624281600449","isReload":false}
{"entryId":"1624279500722","eventOccurred":"2021-06-21T12:53:59.099Z","setId":"1624279500722","isReload":false}
{"entryId":"1615388165028","eventOccurred":"2021-03-10T14:56:34.171Z","setId":"1615388165028","setName":"file48.txt","isReload":false}
>
更新:
这是返回流的服务方法的签名:
Stream<DatasetEvent> allEvents(Optional<Integer> limit);
这是 REST 端点。调用服务方法,结果流的每个元素从 DatasetEvent 映射到 XMLDatasetEvent,结果流作为 Flux 返回。
@GetMapping(value = "/allevents",})
public Flux<XMLDatasetEvent> allEventsXML(
@Parameter(description = "int - Maximum number of dataset events to return") @RequestParam(required = false) Optional<Integer> limit) {
log.info("allEvents(limit={})",limit);
return Flux.fromStream(service.allEvents(limit).map(x -> XMLDatasetEvent.of(x)));
}
上述端点成功返回 JSON 记录流,但仅返回单个 XML 记录,具体取决于 HTTP 调用的接受标头。
Optional 限制从未指定,并且始终为 Optional.empty。这是由具有不同接受标头的一系列请求的日志输出所确认的:
2021-07-23 16:43:29.251 INFO 32184 --- [ctor-http-nio-6] Controller : allEvents(limit=Optional.empty)
2021-07-23 16:44:12.579 INFO 32184 --- [ctor-http-nio-7] Controller : allEvents(limit=Optional.empty)
2021-07-23 16:47:46.615 INFO 32184 --- [ctor-http-nio-8] Controller : allEvents(limit=Optional.empty)
2021-07-23 16:48:33.305 INFO 32184 --- [ctor-http-nio-1] Controller : allEvents(limit=Optional.empty)
2021-07-23 16:49:58.192 INFO 32184 --- [ctor-http-nio-2] Controller : allEvents(limit=Optional.empty)
2021-07-23 16:50:25.445 INFO 32184 --- [ctor-http-nio-3] Controller : allEvents(limit=Optional.empty)
XMLDatasetEvent 具有与 DatasetEvent 相同的公共字段结构,但添加了 @XMLRootElement 注释。它存在的唯一原因是因为 DatasetEvent 位于不同的 git 存储库中,我希望在尝试使 XML 流正常工作时尽量减少影响。
@XmlRootElement(name = "datasetEvent")
public class XMLDatasetEvent {
public String entryId;
public Instant eventOccurred;
public String setId;
public LocalDate setBusinessDate;
public String setName;
public int setSize;
public String sourceSets;
public String status;
public String context = "";
public boolean transform = true;
public boolean isReload = false;
public XMLDatasetEvent() {}
public XMLDatasetEvent(DatasetEvent de) { ... copy fields one by one into a new instance ... }
public static XMLDatasetEvent of(DatasetEvent datasetEvent) {
return new XMLDatasetEvent(datasetEvent);
}
}
复制器项目
域名已更改,所以我重申了完整的问题。
当我使用 accept: application/x-ndjson
调用 REST 端点时,我看到了预期的数据。流中的每条记录都作为单独的 JSON 文档返回。我寻求达到相同的效果,但使用 XML。
$ curl -X 'GET' 'http://localhost:9050/person/all' -H 'accept: application/x-ndjson' -s
{"name":"Robin","age":25}
{"name":"Jack","age":24}
{"name":"Peter","age":23}
{"name":"Sarah","age":22}
当我使用 accept: application/stream+xml
或 accept: application/xml
调用同一个 REST 端点时,我只收到流的第一条记录。
$ curl -X 'GET' 'http://localhost:9050/person/all' -H 'accept: application/stream+xml' -s -N
Robin25adl-benap01(定价)/opt/pricing/apps>$ curl -X 'GET' 'http://localhost:9050/person/all' -H 'accept: application/xml' -s -N
Robin25adl-benap01(定价)/opt/pricing/apps>package net.uk.roos.xmlflux.dto;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="person")
public class Person {
public String name;
public int age;
public Person() {}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
}
控制器在 /person/all 处暴露单个 REST 端点:
import java.util.List;
import java.util.stream.Stream;
@RestController
@RequestMapping("/person")
public class PersonController {
@GetMapping(value = "/all",})
public Flux<Person> all() {
return Flux.fromStream(people());
}
Stream<Person> people() {
List<Person> people = new ArrayList<>();
people.add(new Person("Robin",25));
people.add(new Person("Jack",24));
people.add(new Person("Peter",23));
people.add(new Person("Sarah",22));
return people.stream();
}
}
application.yml 指定端口 9050:
server:
port: 9050
pom.xml 指定 Java 8 和 spring-boot-starter-webflux 依赖项:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>net.uk.roos</groupId>
<artifactId>xmlflux</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>xmlflux</name>
<description>Demo project for Spring WebFlux xml</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
是否有我可以使用的 MIME 类型,以便流中的所有记录都将作为独立的 XML 文档返回?如果没有开箱即用的东西,谁能帮我通过 Web FluxConfigurer 配置一个?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。