Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I want to do the following via Spring WebFlux and a given REST-API:

  1. Retrieve a list of file names (GET /files)
  2. Delete each retrieved file (DELETE /files/local/{file name} for each)

The problem is that I cannot combine both actions to "one" Mono instance. My current implementation is insufficient, because it blocks the Mono instances to execute the api calls immediately insteaf of performing them reactive.

My non reactive implementation:

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
}

How to perform the consecutive web requests in one Mono instance reactive?

like image 794
CGX106 Avatar asked Mar 17 '19 11:03

CGX106


1 Answers

You should chain all of your operations in order to create a reactive stream. Usually you take the output of one operation and use it as input for another operation. Project Reactor provides a lot of map and flatMap operators to accomplish this.

In your example you should retrieve the list of files and then map each element to the delete operation like this:

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();
            });
}
like image 169
stevecross Avatar answered Sep 17 '22 22:09

stevecross