Home > Software engineering >  Exception Handling in Spring webflux
Exception Handling in Spring webflux

Time:09-21

I'm working on a Spring webflux project using reactive streams. I have a usecase as follows and want to know how can it be done in a reactive way.

  @RestController
  public class Example {

    @GetMapping("/greet")
    public Mono<String> Test() {
       return Mono.just("Tim")
               .map(s -> s.toUpperCase())
               .map(s -> s.toLowerCase())
               .doOnSuccess(s -> validate(s)) // usecase is to validate here in middle of the pipeline
               .onErrorResume(ex -> Mono.just("Guest"))
               .map(s -> "Hi, " s);
    }
   
  public void validate(String s) {
    if(s.length() < 5) {throw new RuntimeException("Name is short");}
  }
 
}

I know this is a contrived example, but I have something similar to this. I thought throwing an error will lead to exception on browser screen when the endpoint is hit. But to my surprise it went to onErrorResume() and I got Hi, Guest as response. I thought when throw is used to throw an exception before the reactive pipeline is assembled, it will not use onErrorResume(). What am I missing here?

Also coming to question #2, how can I achieve this if I'm using Mono.error(new RuntimeException("Name is short")) instead of throw new RuntimeException("Name is short")? Can someone please answer my 2 questions. Suggestions to improve code are appreciated.

CodePudding user response:

I thought when throw is used to throw an exception before the reactive pipeline is assembled, it will not use one rrorResume()

Mono::doOnSuccess triggers at execution time when the Mono completes successfully(pipeline is already assembled).

Note that inside intermediate operators like doOnNext or map you are free to throw exceptions as Reactor can transform them into proper error signals since at that point a Mono is already in progress.

how can I achieve this if I'm using Mono.error(new RuntimeException("Name is short")) instead of throw new RuntimeException("Name is short")?

You can replace doOnSuccess and map with the handle operator:

 return Mono.just("Tim")
            .handle((name, sink) -> {
                if(name.length() < 5){
                    sink.error(new RuntimeException("Name is short"));
                } else {
                    sink.next(name.toLowerCase());
                }
            })

CodePudding user response:

For the #1 question you can add a predicate in onErrorResume

.onErrorResume(e -> !(e instanceof RuntimeException), ex -> Mono.just("Guest"))

so that you can filter on which error you can return a predefined result.

For question #2 you can replace doOnSuccess which is a consumer, with flatMap and return Mono forvalidate method:

    public Mono<String> Test() {
        return Mono.just("Tim")
                .map(s -> s.toUpperCase())
                .map(s -> s.toLowerCase())
                .flatMap(s -> validate(s))
                .onErrorResume(e -> !(e instanceof RuntimeException), ex -> Mono.just("Guest"))
                .map(s -> "Hi, "   s);
    }

    public Mono<String> validate(String s) {
        return (s.length() < 5) ?
                Mono.error(() -> new RuntimeException("Name is short")) :
                Mono.just(s);
    }
  • Related