Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring MVC: determine which controller threw exception in @ExceptionHandler

I'm using Spring MVC with Spring Boot and Thymeleaf. I have normal controllers that return the name of a Thymeleaf template and REST controllers annotated with @RepsonseBody.

Let's say I have an EntityNotFoundException that is thrown by some code that is called by the controller. If it is thrown, I want to return a 404 status code and an error page or error message for REST controllers respectively.

My current setup for normal controllers:

@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
      super(message);
    }
}

@Controller
public class FooController {

  @RequestMapping("/foo") public String foo() {
    try {
       ...
    } catch (EntityNotFoundException enfe) {
      throw new ResourceNotFoundException(enfe.getMessage());
    }
    return "foo";
  }
}

For REST cotrollers I don't catch the exception and let a global exception handler pick it up:

@Controller
public class BarController {

  @RequestMapping("/bar")
  @ResponseBody
  public SomeDto bar() throws EntityNotFoundException {
    ...
    return someDto;
  }
}

@ControllerAdvice
public class ExceptionHandlerAdvice {

  @ExceptionHandler(EntityNotFoundException.class)
  public final ResponseEntity<Object> handleEntityNotFoundExceptionEntityNotFoundException enfe) {
    return new ResponseEntity<>(enfe.getMessage, HttpStatus.NOT_FOUND);
  }
}

I don't want to catch and rethrow in my normal controllers as well. The global handler should handle both:

  @ExceptionHandler(EntityNotFoundException.class)
  public final Object handleEntityNotFoundException(EntityNotFoundException enfe) {
    if (/* is REST controller? */) {
      return new ResponseEntity<>(enfe.getMessage(), HttpStatus.NOT_FOUND);
    } else {
      Map<String, Object> model = ImmutableMap.of("message", enfe.getMessage());
      return new ModelAndView("error", model, HttpStatus.NOT_FOUND);
    }
  }

Is there a way to determine where the exception came from, i.e. if the controller is annotated with @ResponseBody ore something alike?

like image 260
Erik Hofer Avatar asked Nov 15 '25 22:11

Erik Hofer


1 Answers

I found a solution. You can inject the current controller method as a HandlerMethod parameter.

  @ExceptionHandler(EntityNotFoundException.class)
  public final Object handleEntityNotFoundException(EntityNotFoundException enfe, HandlerMethod handlerMethod) {

    boolean isRestController = handlerMethod.hasMethodAnnotation(ResponseBody.class)
        || handlerMethod.getBeanType().isAnnotationPresent(ResponseBody.class)
        || handlerMethod.getBeanType().isAnnotationPresent(RestController.class);

    if (isRestController) {
      return new ResponseEntity<>(enfe.getMessage(), HttpStatus.NOT_FOUND);
    } else {
      Map<String, Object> model = ImmutableMap.of("message", enfe.getMessage());
      return new ModelAndView("error", model, HttpStatus.NOT_FOUND);
    }
  }
like image 67
Erik Hofer Avatar answered Nov 17 '25 12:11

Erik Hofer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!