Reputation: 75
I want to do the following via Spring WebFlux and a given REST-API:
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?
Upvotes: 5
Views: 6621
Reputation: 5684
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();
});
}
Upvotes: 9
Reputation: 2228
It should be something like below:
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
}
Upvotes: 0