doOnNext() won't get called Spring Webflux
Asked Answered
W

1

6

I'm a newbie in reactive programming and also Spring Webflux I have a method to get some key from Redis and if this key is null or is not equals to the specified string i want to throw an exception but the nested donOnNext method won't get called and customerRepository.save(customer) get triggered while the exception must be thrown and break the chain. Can someone explain to me how the reactor API behaves in my case?

this is my method:

@Override
public Mono<RegistrationVerificationResDTO> verifyCustomerAndGenerateToken(Mono<VerifyOtpReqDTO> verifyOtpReqDTO) {
    return verifyOtpReqDTO
            .doOnNext(verifyDTO -> reactiveRedisOperations
                    .opsForValue()
                    .get(RedisDictionary.OTP_KEY + verifyDTO.getPhoneNumber())
                    .filter(otp -> otp.equalsIgnoreCase(verifyDTO.getOtp()))
                    .switchIfEmpty(Mono.error(ForbiddenException::new)))
            .map(verifyDTO -> customerRepository.findById(verifyDTO.getId())
                    .orElseThrow(() -> new NotFoundException("Customer not found")))
            .doOnNext(customer -> {
                customer.setVerified(true);
                customerRepository.save(customer);
            })
            .map(customer -> new RegistrationVerificationResDTO().setAccessToken("accessToken")
                    .setRefreshToken("refreshToken")
                    .setCustomer(customer));
}

UPDATE: I realized if we create another publisher inside the doOnNext method because the spring just subscribes to the most outer publisher the inner one won't get triggered I have updated my code but it still doesn't work.

Wendellwendi answered 3/3, 2020 at 19:49 Comment(8)
You are calling subscribe () at some point?Vassallo
@Vassallo Spring calls it automaticallyWendellwendi
On the returned Mono? How?Vassallo
@Vassallo #50795571Wendellwendi
doOnNext is just callback that says what to do when Mono above completed successfully,.. usually we log something or update some value, but it is not used to return something or throw exception.. so to summarize, your exception in doOnNext is swallowed If you want to throw exception then do it different, i.e. check here: #53595920Facing
@Facing I have changed it to: verifyOtpReqDTO .doOnNext(verifyDTO -> reactiveRedisOperations .opsForValue() .get(RedisDictionary.OTP_KEY + verifyDTO.getPhoneNumber()) .switchIfEmpty(Mono.error(ForbiddenException::new)) .filter(otp -> otp.equalsIgnoreCase(verifyDTO.getOtp())) .switchIfEmpty(Mono.error(ForbiddenException::new))) .map(... but it still doesn't work!Wendellwendi
can you update the question with the new code that doesn't check for nulls ? your comment above is not very readable.Airfoil
@SimonBaslé doneWendellwendi
H
4

I'm guessing you're saying this "doesn't work" because you can't observe the saved customer in DB, even after the (correct) changes you've made to the second (innermost) doOnNext?

The third doOnNext is problematic: customerRepository.save(customer) is a NO-OP assuming customerRepository is a reactive repository, because the (lazy) Mono is neither attached to the main sequence nor subscribed to.

Simply replace that doOnNext with flatMap (and keep your changes to the innermost doOnNext with switchIfEmpty as well) to make it part of the reactive chain that Spring will subscribe to.

Heyday answered 6/3, 2020 at 9:20 Comment(3)
No, your guess is not correct the problem is the customer saves in the DB but my expectation is thrown the exception before that, consider ”nested” word in my question. But thank you for your suggestion in the third paragraph I will try it and I'll keep you updated. My other question is about this sentence that you have mentioned: to make it part of the reactive chain that spring will subscribe to. Based on my knowledge spring subscribes to the chain automatically and there's no difference between doonnext or flatmap could you please clarify your meaning by that sentence?Wendellwendi
there is a HUGE difference between handling a Mono/Flux inside a doOnNext and inside a flatMap: Spring does subscribe to the outer Mono or Flux that your controller returns, but that subscription only propagates to publishers that are links in the chain. Generating a Publisher inside a doOnNext doesn't make it a link the chain, while returning a Publisher from flatMap does.Airfoil
I updated my code in the question description could you please review it?Wendellwendi

© 2022 - 2024 — McMap. All rights reserved.