如何在 Spring WebFlux 中的一个 Mono 中基于另一个请求执行连续的 Web 请求?

How to perform consecutive web requests based on another request in one Mono in Spring WebFlux?

我想通过 Spring WebFlux 和给定的 REST-API 执行以下操作:

  1. 检索文件名列表 (GET /files)
  2. 删除每个检索到的文件(每个删除 /files/local/{文件名})

问题是我无法将这两个操作组合到 "one" Mono 实例。我当前的实现是不够的,因为它阻止 Mono 实例立即执行 api 调用,而不是被动地执行它们。

我的非响应式实现:

public Mono cleanUpUploadedFiles() {    
    WebClient webClient = this.getWebClient();

    // get all files / directories
    Mono<FilesOverview> filesOverviewMono = this.getResource("/files", FilesOverview.class);
    FilesOverview filesOverview = filesOverviewMono.block(); // TODO: prevent blocking mono

    // delete file / directory one by one
    for (FileOverview file : filesOverview.getFiles()) {
        ClientResponse clientResponse;

        clientResponse = webClient
                .delete()
                .uri(String.format("/files/local/%s", file.getName()))
                .exchange()
                .block(); // TODO: prevent blocking mono
        if (clientResponse == null) {
            return Mono.error(new MyException(String.format("could not execute rest call to delete uploaded files with uuid %s", file.getName())));
        }

        HttpStatus clientResponseStatusCode = clientResponse.statusCode();
        if (clientResponseStatusCode.isError()) {
            return Mono.error(new MyException(String.format("cannot delete uploaded files with uuid %s", file.getName())));
        }
    }

    return Mono.empty(); // TODO: return Mono instance performing everything reactive without blocking
}

如何在一个 Mono 实例中响应式执行连续的 Web 请求?

应该如下所示:

public Mono cleanUpUploadedFiles() {    
WebClient webClient = this.getWebClient();

// get all files / directories
Mono<FilesOverview> filesOverviewMono = this.getResource("/files", FilesOverview.class);
return filesOverviewMono.flatMap(file ->{
        return webClient
                .delete()
                .uri(String.format("/files/local/%s", file.getName()))
                .exchange()
                .flatMap()//normal scenario
                .onErrorResume()//error
                .switchIfEmpty();//empty body
                //each of these will give you a Mono, so you would need to 
                //transform all of them to finally give a Mono as needed by 
                //the method

    }).flatMap();//transform

}

您应该链接所有操作以创建反应流。 通常,您获取一个操作的输出并将其用作另一个操作的输入。 Project Reactor 提供了许多 mapflatMap 运算符来完成此操作。

在您的示例中,您应该检索文件列表,然后将每个元素映射到删除操作,如下所示:

public Mono<Void> cleanUpUploadedFiles() {
    return getResource("/files", FilesOverview.class) // Retrieve the file list
            .flatMapIterable(FilesOverview::getFiles) // Create a Flux from the file list
            .map(FileOverview::getName) // Map the file overview to the file name
            .flatMap(this::deleteFile) // Delete the file
            .then(); // Just return a Mono<Void>
}

private Mono<Void> deleteFile(String fileName) {
    return getWebClient()
            .delete()
            .uri("/files/local/{fileName}", fileName)
            .exchange() // Perform the delete operation
            .onErrorMap(e -> new MyException(String.format("could not execute rest call to delete uploaded files with uuid %s", fileName))) // Handle errors
            .map(ClientResponse::statusCode) // Map the response to the status code
            .flatMap(statusCode -> {
                // If the operation was not successful signal an error
                if (statusCode.isError()) {
                    return Mono.error(new MyException(String.format("cannot delete uploaded files with uuid %s", fileName)));
                }

                // Otherwise return a Mono<Void>
                return Mono.empty();
            });
}