Exception handling in Spring Webflux?
In Spring WebFlux, exception handling can be managed in several ways, allowing you to handle errors in a reactive programming style. Here are some common approaches to handle exceptions in a Spring WebFlux application:
@ControllerAdvice
You can create a global exception handler using the @ControllerAdvice
annotation. This allows you to handle exceptions thrown by any controller in a centralized manner.
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import reactor.core.publisher.Mono;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
public Mono<ResponseEntity<String>> handleCustomException(CustomException ex) {
return Mono.just(ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(ex.getMessage()));
}
@ExceptionHandler(Exception.class)
public Mono<ResponseEntity<String>> handleGenericException(Exception ex) {
return Mono.just(ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("An unexpected error occurred"));
}
}
WebExceptionHandler
For more fine-grained control, you can implement the WebExceptionHandler
interface. This allows you to handle exceptions at a lower level.
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;
@Component
@Order(-2) // Ensure this handler is called before the default handlers
public class CustomWebExceptionHandler implements WebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (ex instanceof CustomException) {
return ServerResponse
.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(ex.getMessage())
.flatMap(response -> response.writeTo(exchange, new Context()));
}
return ServerResponse
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.bodyValue("An unexpected error occurred")
.flatMap(response -> response.writeTo(exchange, new Context()));
}
}
onErrorResume
and doOnError
In your reactive pipelines, you can use operators like onErrorResume
and doOnError
to handle exceptions.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class MyController {
@GetMapping("/example")
public Mono<String> example() {
return someServiceCall()
.onErrorResume(CustomException.class, ex -> Mono.just("Handled Custom Exception: " + ex.getMessage()))
.doOnError(ex -> System.err.println("Error occurred: " + ex.getMessage()));
}
private Mono<String> someServiceCall() {
// Simulate a service call that may throw an exception
return Mono.error(new CustomException("Something went wrong"));
}
}
You can also customize the error response by implementing ErrorAttributes
. This is useful if you want to modify the default error response structure.
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.function.server.ServerRequest;
import reactor.core.publisher.Mono;
import java.util.Map;
@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace);
// Customize the error attributes here
errorAttributes.put("customMessage", "This is a custom error message");
return errorAttributes;
}
}
These are some of the common ways to handle exceptions in Spring WebFlux. Depending on your application's requirements, you can choose the approach that best fits your needs. Using @ControllerAdvice
is often the simplest way to handle exceptions globally, while WebExceptionHandler
provides more control over the error handling process.