X2015jpp
X2015jpp

Reputation: 143

Spring Webflux: Extract value from Mono

I am new to spring webflux and am trying to perform some arithmetic on the values of two monos. I have a product service that retrieves account information by calling an account service via webClient. I want to determine if the current balance of the account is greater than or equal to the price of the product.

Mono<Account> account = webClientBuilder.build().get().uri("http://account-service/user/accounts/{userId}/",userId)
.retrieve().bodyToMono(Account.class);


//productId is a path variable on method
Mono<Product> product =this.productService.findById(productId);

When I try to block the stream I get an error

block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2

//Causes Error
Double accountBalance = account.map(a->a.getBalance()).block():

Double productPrice = product.map(p->p.getPrice()).block();

///Find difference, send response accordingly....

Is this the correct approach of there is another, better way to achieve this? I was also thinking something along the lines of:

Mono<Double> accountBalance = account.map(a->a.getBalance()):

Mono<Double> productPrice = product.map(p->p.getPrice());

Mono<Double> res = accountBalance.zipWith(productPrice,(b,p)-> b-p);

//Something after this.....

Upvotes: 4

Views: 9887

Answers (1)

Bartosz Kiebdaj
Bartosz Kiebdaj

Reputation: 141

You can't use block method on main reactor thread. This is forbidden. block may work when publish mono on some other thread but it's not a case.

Basically your approach with zipping two monos is correct. You can create some helper method to do calculation on them. In your case it may look like:

public boolean isAccountBalanceGreater(Account acc, Product prd) {
   return acc.getBalance() >= prd.getPrice();
}

And then in your Mono stream you can pass method reference and make it more readable.

Mono<Boolean> result = account.zipWith(productPrice, this::isAccountBalanceGreater)

The question is what you want to do with that information later. If you want return to your controller just true or false that's fine. Otherwise you may need some other mappings, zippings etc.

Update

return account.zipWith(productPrice, this::createResponse);

...

ResponseEntity createResponse(Account acc, Product prd) {
   int responseCode = isAccountBalanceGreater(acc, prd) ? 200 : 500;
   return ResponseEntity.status(responseCode).body(prd);
}

Upvotes: 2

Related Questions