1

I am trying to build a GlobalExceptionHandler which extends AbstractErrorWebExceptionHandler. I have custom exception classes which extends RuntimeException. When I throw an exception I build a custom object which I need in the GlobalExceptionHandler to log and return custom json.

I am used to the MVC way of exception handing and webflux is new to me.

public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

 public GlobalErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ApplicationContext applicationContext, ServerCodecConfigurer configurer) {
  super(errorAttributes, resourceProperties, applicationContext);
  this.setMessageWriters(configurer.getWriters());
 }

 @Override
 protected RouterFunction < ServerResponse > getRoutingFunction(ErrorAttributes errorAttributes) {
  // code here gets executed but I do not have access to custom objects I build when throwing exception.
  return ...
 }

 // This code does not get executed. 
 @ExceptionHandler(CustomException.class)
 public Mono < ServerResponse > customException(CustomException ex) {
  // ex contains custom attributes I have created which needs to be logger here and returned 
  // use ex here to build and return custom json.
  return ...;
 }

When I throw an exception I build custom object and was hoping the global exception handler would contain this object.

if(error){
CustomExceptionObject o = CustomExceptionObject.builder()...build();
throw new CustomException( o ); // i need to access this in the exception handler.
}

Is this the correct way to handle exceptions or does webflux have another way to handle situations like these?

Albin Vinoy
  • 51
  • 1
  • 6
  • why not just use a `@ControllerAdvice` stated in the Webflux documentation https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-ann-controller-exceptions – Toerktumlare May 08 '20 at 11:49
  • I tried both ControllerAdvice and RestControllerAdvice. I am using routerfunctions btw. – Albin Vinoy May 08 '20 at 16:35
  • Could be nice if that was included in the question? – Toerktumlare May 08 '20 at 17:22
  • you should not `throw` exceptions in a webflux application. You handle exceptions by either returning `empty`, or `error`. You can achive the same functionality as a `@ControllerAdvice` by using WebHandlers here https://stackoverflow.com/questions/43575538/what-is-the-right-way-to-handle-errors-in-spring-webflux – Toerktumlare May 09 '20 at 21:26
  • you can just register a WebExceptionHandler bean with your own Order https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-exception-handler – Toerktumlare May 09 '20 at 21:28

2 Answers2

1

For the future readers, the class AbstractErrorWebExceptionHandler exposes a method Throwable getError(ServerRequest request) which can be used to get the underlying error within your implementated class.

Here is a sample:

@Component
@Order(-2)
public class ReactiveWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    // constructor

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        // @formatter:off
        ErrorAttributeOptions errorAttributeOptions = ErrorAttributeOptions.of(Include.values());
        Map<String, Object> errorAttributes = getErrorAttributes(request, errorAttributeOptions);

        Throwable error = getError(request);
        final String message;

        if (error instanceof UnsupportedMediaTypeStatusException exception) {
            message = getUnsupportedMediaTypeMessage(exception);
        } else {
            message = (String) errorAttributes.get("message");
        }

        Map<String, Object> responseBody = Map.of("message", message);

        int statusCode = Integer.parseInt(Objects.toString(errorAttributes.get("status")));
        HttpStatus httpStatus = HttpStatus.valueOf(statusCode);

        return ServerResponse.status(httpStatus)
            .contentType(MediaType.APPLICATION_NDJSON)
            .body(BodyInserters.fromValue(responseBody));
        // @formatter:on
    }

    private String getUnsupportedMediaTypeMessage(UnsupportedMediaTypeStatusException exception) {
        // implementation removed for brevity
    }
}
Tapas Bose
  • 28,796
  • 74
  • 215
  • 331
0

Although, you hopefully came up with a solution in in the meantime, this is probably what you are looking for (chapter 4):

https://www.baeldung.com/spring-webflux-errors

Scrolling to the end of the article, there is a link to a git repository containing some more detailed code.