【问题标题】:How to perform consecutive web requests based on another request in one Mono in Spring WebFlux?如何在 Spring WebFlux 中基于一个 Mono 中的另一个请求执行连续的 Web 请求?
【发布时间】:2019-08-07 22:34:22
【问题描述】:

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

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

问题是我不能将这两个动作结合到“一个”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 请求?

【问题讨论】:

    标签: java reactive-programming spring-webflux reactor


    【解决方案1】:

    应该是这样的:

    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
    

    }

    【讨论】:

    • 感谢您的回答。第一个 api 调用将文件列表作为 FilesOverview 返回。我错过了删除 filesOverviewMono.flatMap 的 lambda 中的每个 (!) 文件。如果之后要删除一个文件,该示例将匹配,我必须从 filesOverview.getFiles() 中删除 n 个文件。
    【解决方案2】:

    您应该链接所有操作以创建反应式流。 通常,您获取一个操作的输出并将其用作另一操作的输入。 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();
                });
    }
    

    【讨论】:

    • 感谢您提供出色的样本 :)
    猜你喜欢
    • 1970-01-01
    • 2019-01-02
    • 2017-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 2019-09-19
    • 2020-10-23
    相关资源
    最近更新 更多