Micronaut 寫入響應(yīng)數(shù)據(jù)

2023-03-06 17:32 更新

Reactively Writing Response Data(反應(yīng)性地寫入響應(yīng)數(shù)據(jù))

Micronaut 的 HTTP 服務(wù)器通過返回一個 Publisher 來支持編寫響應(yīng)數(shù)據(jù)塊,該 Publisher 發(fā)出可以編碼為 HTTP 響應(yīng)的對象。

下表總結(jié)了示例返回類型簽名以及服務(wù)器為處理它們而表現(xiàn)出的行為:

返回類型 描述

Publisher<String>

將每個內(nèi)容塊作為字符串發(fā)出的發(fā)布者

Flux<byte[]>

將每個內(nèi)容塊作為 byte[] 發(fā)出而不阻塞的 Flux

Flux<ByteBuf>

將每個塊作為 Netty ByteBuf 發(fā)出的 Reactor Flux

Flux<Book>

發(fā)出 POJO 時,每個發(fā)出的對象默認(rèn)編碼為 JSON,不會阻塞

Flowable<byte[]>

將每個內(nèi)容塊作為 byte[] 發(fā)出而不阻塞的 Flux

Flowable<ByteBuf>

將每個塊作為 Netty ByteBuf 發(fā)出的 Reactor Flux

Flowable<Book>

發(fā)出 POJO 時,每個發(fā)出的對象默認(rèn)編碼為 JSON,不會阻塞

當(dāng)返回一個反應(yīng)類型時,服務(wù)器使用分塊的傳輸編碼并保持寫入數(shù)據(jù),直到調(diào)用 Publisher onComplete 方法。

服務(wù)器從發(fā)布者請求一個項目,寫入它,然后請求下一個,控制背壓。

由發(fā)布者的實現(xiàn)來安排任何可能因訂閱發(fā)布者而完成的阻塞 I/O 工作。

要使用 Project Reactor 的 Flux 或 Mono,您需要將 Micronaut Reactor 依賴項添加到您的項目以包含必要的轉(zhuǎn)換器。

要使用 RxJava 的 Flowable、Single 或 Maybe,您需要將 Micronaut RxJava 依賴項添加到您的項目以包含必要的轉(zhuǎn)換器。

執(zhí)行阻塞 I/O

在某些情況下,您可能希望集成一個不支持非阻塞 I/O 的庫。

Writable

在這種情況下,您可以從任何控制器方法返回一個 Writable 對象。 Writable 接口具有各種簽名,允許寫入傳統(tǒng)的阻塞流,如 Writer 或 OutputStream。

當(dāng)返回 Writable 時,阻塞的 I/O 操作被轉(zhuǎn)移到 I/O 線程池,因此 Netty 事件循環(huán)不會被阻塞。

以下示例演示了如何將此 API 與 Groovy 的 SimpleTemplateEngine 結(jié)合使用來編寫服務(wù)器端模板:

使用可寫執(zhí)行阻塞 I/O

 Java Groovy  Kotlin 
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");
        }
    }
}
import groovy.text.SimpleTemplateEngine
import groovy.text.Template
import io.micronaut.core.io.Writable
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")
class TemplateController {

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

    @Get(value = "/welcome", produces = MediaType.TEXT_PLAIN)
    Writable render() { // (2)
        { writer ->
            template.make( // (3)
                    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")
        }
    }
}
import groovy.text.SimpleTemplateEngine
import groovy.text.Template
import io.micronaut.core.io.Writable
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.server.exceptions.HttpServerException
import java.io.Writer

@Controller("/template")
class TemplateController {

    private val templateEngine = SimpleTemplateEngine()
    private val template = initTemplate() // (1)

    @Get(value = "/welcome", produces = [MediaType.TEXT_PLAIN])
    internal fun render(): Writable { // (2)
        return { writer: Writer ->
            template.make( // (3)
                    mapOf(
                        "firstName" to "Fred",
                        "lastName" to "Flintstone"
                    )
            ).writeTo(writer)
        } as Writable
    }

    private fun initTemplate(): Template {
        return try {
            templateEngine.createTemplate(
                "Dear \$firstName \$lastName. Nice to meet you."
            )
        } catch (e: Exception) {
            throw HttpServerException("Cannot create template")
        }
    }
}
  1. 控制器創(chuàng)建一個簡單的模板

  2. 控制器方法返回一個 Writable
  3. 返回的函數(shù)接收 Writer 并在模板上調(diào)用 writeTo。

InputStream

另一種選擇是返回輸入流。這對于許多與公開流的其他 API 交互的場景很有用。

使用 InputStream 執(zhí)行阻塞 I/O

 Java Groovy  Kotlin 
@Get(value = "/write", produces = MediaType.TEXT_PLAIN)
InputStream write() {
    byte[] bytes = "test".getBytes(StandardCharsets.UTF_8);
    return new ByteArrayInputStream(bytes); // (1)
}
@Get(value = "/write", produces = MediaType.TEXT_PLAIN)
InputStream write() {
    byte[] bytes = "test".getBytes(StandardCharsets.UTF_8);
    new ByteArrayInputStream(bytes) // (1)
}
@Get(value = "/write", produces = [MediaType.TEXT_PLAIN])
fun write(): InputStream {
    val bytes = "test".toByteArray(StandardCharsets.UTF_8)
    return ByteArrayInputStream(bytes) // (1)
}
  1. 返回輸入流,其內(nèi)容將是響應(yīng)主體

如果在事件循環(huán)上執(zhí)行控制器方法,流的讀取將被卸載到 IO 線程池。

404 Responses

通常,當(dāng)您在持久層或類似場景中找不到某個項目時,您希望響應(yīng) 404(未找到)。

請參閱以下示例:

 Java Groovy  Kotlin 
@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)
    }
}
@Controller("/books")
class BooksController {

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

    @Get("/maybestock/{isbn}")
    Mono<Map> maybestock(String isbn) {
        Mono.empty() //(2)
    }
}
@Controller("/books")
class BooksController {

    @Get("/stock/{isbn}")
    fun stock(isbn: String): Map<*, *>? {
        return null //(1)
    }

    @Get("/maybestock/{isbn}")
    fun maybestock(isbn: String): Mono<Map<*, *>> {
        return Mono.empty() //(2)
    }
}
  1. 返回 null 會觸發(fā) 404(未找到)響應(yīng)。

  2. 返回空的 Mono 會觸發(fā) 404(未找到)響應(yīng)。

如果內(nèi)容類型為 JSON,則使用空 Publisher 或 Flux 進行響應(yīng)會導(dǎo)致返回一個空數(shù)組。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號