what is the right way to handle errors in spring-webflux

Why not do it the old fashioned way by throwing exceptions from handler functions and implementing your own WebExceptionHandler to catch 'em all:

@Component
class ExceptionHandler : WebExceptionHandler {
    override fun handle(exchange: ServerWebExchange?, ex: Throwable?): Mono<Void> {
        /* Handle different exceptions here */
        when(ex!!) {
            is NoSuchElementException -> exchange!!.response.statusCode = HttpStatus.NOT_FOUND
            is Exception -> exchange!!.response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR
        }

        /* Do common thing like logging etc... */

        return Mono.empty()
    }
}

Above example is in Kotlin, since I just copy pasted it from a project I´m currently working on, and since the original question was not tagged for java anyway.


If you think, router functions are not the right place to handle exceptions, you throw HTTP Exceptions, that will result in the correct HTTP Error codes. For Spring-Boot (also webflux) this is:

  import org.springframework.http.HttpStatus;
  import org.springframework.web.server.ResponseStatusException;
  .
  .
  . 

  new ResponseStatusException(HttpStatus.NOT_FOUND,  "Collection not found");})

spring securities AccessDeniedException will be handled correctly, too (403/401 response codes).

If you have a microservice, and want to use REST for it, this can be a good option, since those http exceptions are quite close to business logic, and should be placed near the business logic in this case. And since in a microservice you shouldn't have to much businesslogic and exceptions, it shouldn't clutter your code, too... (but of course, it all depends).


Spring 5 provides a WebHandler, and in the JavaDoc, there's the line:

Use HttpWebHandlerAdapter to adapt a WebHandler to an HttpHandler. The WebHttpHandlerBuilder provides a convenient way to do that while also optionally configuring one or more filters and/or exception handlers.

Currently, the official documentation suggests that we should wrap the router function into an HttpHandler before booting up any server:

HttpHandler httpHandler = RouterFunctions.toHttpHandler(routerFunction);

With the help of WebHttpHandlerBuilder, we can configure custom exception handlers:

HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(toHttpHandler(routerFunction))
  .prependExceptionHandler((serverWebExchange, exception) -> {

      /* custom handling goes here */
      return null;

  }).build();