Throwing an exception vs Mono.error() in Spring webflux
Asked Answered
H

2

11

I'm working on a Spring webflux project and I want to understand the difference between throwing an exception vs using Mono.error().

If there is a validation class like this for example:

 public class NameValidator {

   public static boolean isValid(String name) {
     if(StringUtils.isEmpty(name)) {throw new RuntimeException("Invalid name");}
     return true;
  }
}



public class NameValidator2 {

   public static Mono<Object> isValid(String name) {
     if(StringUtils.isEmpty(name)) {
          return Mono.error(new RuntimeException("Invalid name"));}
     return Mono.just(true);

  }

 }

What are the pros & cons with each approach. When to use one over the other while working with reactive streams using spring webflux?

Huei answered 14/9, 2021 at 13:39 Comment(0)
P
14

As @Joao already stated, the recommended way to deal with an error is to call the error method on a Publisher(Mono.error/Flux.error).

I would like to show you an example in which the traditional throw does not work as you may expect:

public void testErrorHandling() {
    Flux.just("a", "b", "c")
        .flatMap(e -> performAction()
            .onErrorResume(t -> {
                System.out.println("Error occurred");
                return Mono.empty();
            }))
        .subscribe();
}

Mono<Void> performAction() {
    throw new RuntimeException();
}

The onErrorResume operator will never be executed because the exception is thrown before Mono is assembled.

In general, throw works just like Mono.error() (Reactor catches your exception and transforms it into a Mono.error). The example above does not work because the exception is thrown during the assembly period before Mono is created.

Piper answered 14/9, 2021 at 21:15 Comment(4)
Can I say that an exception that uses throw rather than Mono.error() will not work with any onErrorXXX operators?Huei
In general, throw works just like Mono.error() (Reactor catches your exception and transforms it into a Mono.errror. The example above does not work because the exception is thrown during the assembly period before Mono is created.Piper
Do you mean I can first create the pipeline and assign it to a variable and call subscribe() on that variable? Will that work with throw?Huei
@Ikatiforis Can you put that comment at the top of your answer? I feel like that is the actual answer to the question, while the answer you wrote is just (1) general advice and (2) pointing out a footgunScouring
W
4

Basically you will have the same result in the end and no difference between the two options (maybe performance wise but I have not found anything backing this opinion so I guess it can be negligible. The only “difference” is that Mono.error follows the Reactive Streams specification and throwing an exception as is does not (read more at https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md#2.13). However it is not prohibited, but if you like to follow standards and specifications (I guess you do) you should consider using Mono.error.

Wilfordwilfred answered 14/9, 2021 at 18:34 Comment(1)
can you please specify which point exactly states that in the specifications? Point 2.13 which appears at the end of your link does not seem to say anything about throwing Mono.error instead of throwing exceptions.Spread

© 2022 - 2024 — McMap. All rights reserved.