Как выполнять последовательные веб-запросы на основе другого запроса в одном Mono в Spring WebFlux? - PullRequest
0 голосов
/ 17 марта 2019

Я хочу сделать следующее с помощью Spring WebFlux и данного REST-API:

  1. Получить список имен файлов (GET / files)
  2. Удалить каждый полученный файл (DELETE / files / local / {имя файла} для каждого)

Проблема в том, что я не могу объединить оба действия в «один» экземпляр 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
}

Как выполнить последовательный вебзапросы в одном моно экземпляре реагируют?

Ответы [ 2 ]

1 голос
/ 18 марта 2019

Вы должны объединить все свои операции, чтобы создать реактивный поток.Обычно вы берете выходные данные одной операции и используете их в качестве входных данных для другой операции.Project Reactor предоставляет множество операторов map и flatMap, чтобы выполнить это.

В вашем примере вы должны получить список файлов, а затем сопоставить каждый элемент с операцией удаления, например:

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();
            });
}
0 голосов
/ 17 марта 2019

Это должно быть примерно так:

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

}

...