Skip to content

Handling Web Exceptions

When exposing RESTful APIs you can choose between @RestController and functional RouterFunction routes.

The former is simple. If you have some experience with Spring WebMvc, it is easy to update your knowledge. Just need to use newer Reactor specific Mono and Flux instead in your codes.

The exception handling in reactive @RestController is similar to the one in WebMVC.

  1. Create an Exception class.

    public class PostNotFoundException extends RuntimeException {
        public PostNotFoundException(Long id) {
            super("Post:" + id +" is not found.");
        }
    }
    
  2. Throw exception in your codes.

    @GetMapping(value = "/{id}")
    public Mono<Post> get(@PathVariable(value = "id") Long id) {
        return this.posts.findById(id).switchIfEmpty(Mono.error(new PostNotFoundException(id)));
    }
    
  3. Create a standalone @ControllerAdvice annotated class or @ExceptionHandler annotated method in your controller class to handle the exceptions.

    @RestControllerAdvice
    @Slf4j
    class RestExceptionHandler {
    
        @ExceptionHandler(PostNotFoundException.class)
        public ResponseEntity<?> postNotFound(PostNotFoundException ex) {
            log.debug("handling exception: {}", ex.toString());
            return ResponseEntity.notFound().build();
        }
    
    }
    

@RestControllerAdvice only works for @RestController.

If you are using RouterFunction, create a WebExceptionHandler bean to handle it manually.

@Bean
public WebExceptionHandler exceptionHandler() {
    return (ServerWebExchange exchange, Throwable ex) -> {
        if (ex instanceof PostNotFoundException) {
            exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
            return exchange.getResponse().setComplete();
        }
        return Mono.error(ex);
    };
}

Note

WebExceptionHandler is a low-level API. It also works for the exception handling in the annotated controller case — the @ExceptionHandler mechanism itself is implemented as a chain of WebExceptionHandlers.

Registering a WebExceptionHandler in Spring Boot

In a Spring Boot application, register the handler as a @Component with an explicit @Order(-2) so it runs before the default ErrorWebExceptionHandler (which has order -1):

@Component
@Order(-2)
class RestWebExceptionHandler implements WebExceptionHandler {

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        if (ex instanceof PostNotFoundException) {
            exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);

            // marks the response as complete and forbids writing to it
            return exchange.getResponse().setComplete();
        }
        return Mono.error(ex);
    }
}

You can explore the complete codes in spring-reactive-sample/exception-handler and spring-reactive-sample/boot-exception-handler.

Tip

Spring supports the standard error response format defined by RFC 9457. See Problem Details for the ProblemDetail based approach.