跳到主要内容

6.20 书写响应数据

响应式书写响应数据

Micronaut 的 HTTP 服务器通过返回一个 Publisher 来支持写入响应数据块,该发布服务器发送可以编码到 HTTP 响应的对象。

下表总结了示例返回类型签名以及服务器处理它们时表现出的行为:

响应类型描述
Publisher<String>一个 Publisher 将每个内容块作为字符串发送
Flux<byte[]>一个 Flux 将每个内容块作为 byte[] 无阻塞的发送
Flux<ByteBuf>一个 Reactor Flux 将每个内容块作为 Netty ByteBuf 发送
Flux<Book>当发送 POJO 时,默认情况下,每个发出的对象都被无阻塞的编码为 JSON
Flowable<byte[]>一个 Flux 将每个内容块作为 byte[] 无阻塞的发送
Flowable<ByteBuf>一个 Reactor Flux 将每个内容块作为 Netty ByteBuf 发送
Flowable<Book>当发送 POJO 时, 默认情况下,每个发出的对象都被无阻塞的编码为 JSON

当返回响应式类型时,服务器使用分块的 Transfer-Encoding 并继续写入数据,直到调用 Publisher onComplete 方法为止。

服务器从 Publisher 请求一个项目,写入该项目,然后请求下一个项目(控制背压)。

注意

Publisher 的实现来调度任何阻塞 I/O 工作,这些工作由订阅发布器的结果来完成的。

注意

要使用 Project ReactorFluxMono,你需要在项目中添加 Micronaut Reactor 依赖,以引入必需的转换器。

注意

要使用 RxJavaFlowableSingleMaybe,你需要将 Micronaut RxJava 依赖项添加到你的项目中,以引入必需的转换器。

执行阻塞 I/O

在某些情况下,你可能希望集成不支持非阻塞I/O的库。

Writable

在这种情况下,你可以从任何控制器方法返回一个 Writable 对象。Writable 接口有各种签名,允许写入传统的阻塞流,如 WriterOutputStream

当返回 Writable 时,阻塞 I/O 操作会转移到 I/O 线程池,因此不会阻塞 Netty 事件循环。

注意

有关如何配置 I/O 线程池以满足应用程序要求的详细信息,参阅配置服务器线程池部分。

以下示例演示了如何将此 API 与 Groovy 的 SimpleTemplateEngine 结合使用来编写服务器端模板:

使用 Writable 执行阻塞 I/O

import groovy.text.SimpleTemplateEngine;
import groovy.text.Template;
import io.micronaut.core.io.Writable;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.server.exceptions.HttpServerException;

@Controller("/template")
public class TemplateController {

private final SimpleTemplateEngine templateEngine = new SimpleTemplateEngine();
private final Template template = initTemplate(); // (1)

@Get(value = "/welcome", produces = MediaType.TEXT_PLAIN)
Writable render() { // (2)
return writer -> template.make( // (3)
CollectionUtils.mapOf(
"firstName", "Fred",
"lastName", "Flintstone"
)
).writeTo(writer);
}

private Template initTemplate() {
try {
return templateEngine.createTemplate(
"Dear $firstName $lastName. Nice to meet you."
);
} catch (Exception e) {
throw new HttpServerException("Cannot create template");
}
}
}
  1. 控制器创建一个简单的模板
  2. 控制器方法返回一个 Writable
  3. 返回的函数接收一个 Writer 并调用模板上的 writeTo

InputStream

另一个选项是返回一个输入流。这对于许多与暴露流的其他 API 交互场景非常有用。

使用 InputStream 执行阻塞 I/O

@Get(value = "/write", produces = MediaType.TEXT_PLAIN)
InputStream write() {
byte[] bytes = "test".getBytes(StandardCharsets.UTF_8);
return new ByteArrayInputStream(bytes); // (1)
}
  1. 返回输入流,其内容将作为响应体
注意

如果在事件循环上执行控制器方法,则流的读取将被卸载到IO线程池。

404 响应

通常,当你在持久层或类似场景中找不到项目时,你希望响应 404(Not Found,未找到)。

请参见以下示例:

@Controller("/books")
public class BooksController {

@Get("/stock/{isbn}")
public Map stock(String isbn) {
return null; //(1)
}

@Get("/maybestock/{isbn}")
@SingleResult
public Publisher<Map> maybestock(String isbn) {
return Mono.empty(); //(2)
}
}
  1. 返回 null 将触发 404(Not Found,未找到)响应。
  2. 返回空 Mono 会触发 404(Not Found,未找到)响应。
注意

如果内容类型为 JSON,则使用空的 PublisherFlux 进行响应会导致返回空数组。

英文链接