I want to let HandlerExceptionResolver resolve any Exceptions that I don't explicit catch via @ExceptionHandler
annotation.
Anyways, I want to apply specific logic on those exceptions. Eg send a mail notification or log additionally. I can achieve this by adding a @ExceptionHandler(Exception.class)
catch as follows:
@RestControllerAdvice
public MyExceptionHandler {
@ExceptionHandler(IOException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Object io(HttpServletRequest req, Exception e) {
return ...
}
@ExceptionHandler(Exception.class)
public Object exception(HttpServletRequest req, Exception e) {
MailService.send();
Logger.logInSpecificWay();
//TODO how to continue in the "normal" spring way with HandlerExceptionResolver?
}
}
Problem: if I add @ExceptionHandler(Exception.class)
like that, I can catch those unhandled exceptions.
BUT I cannot let spring
continue the normal workflow with HandlerExceptionResolver
to create the response ModelAndView
and set a HTTP STATUS code automatically.
Eg if someone tries a POST
on a GET
method, spring by default would return a 405 Method not allowed
. But with an @ExceptionHandler(Exception.class)
I would swallow this standard handling of spring...
So how can I keep the default HandlerExceptionResolver
, but still apply my custom logic?
To provide a complete solution: it works just by extending ResponseEntityExceptionHandler
, as that handles all the spring-mvc
errors.
And the ones not handled can then be caught using @ExceptionHandler(Exception.class)
.
@RestControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> exception(Exception ex) {
MailService.send();
Logger.logInSpecificWay();
return ... custom exception
}
}
Well, I was facing the same problem some time back and have tried several ways like extending ResponseEntityExceptionHandler
but all them were solving some problems but creating other ones.
Then I have decided to go with a custom solution which was also allowing me to send additional information and I have written below code
@RestControllerAdvice
public class MyExceptionHandler {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@ExceptionHandler(NumberFormatException.class)
public ResponseEntity<Object> handleNumberFormatException(NumberFormatException ex) {
return new ResponseEntity<>(getBody(BAD_REQUEST, ex, "Please enter a valid value"), new HttpHeaders(), BAD_REQUEST);
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<Object> handleIllegalArgumentException(IllegalArgumentException ex) {
return new ResponseEntity<>(getBody(BAD_REQUEST, ex, ex.getMessage()), new HttpHeaders(), BAD_REQUEST);
}
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<Object> handleAccessDeniedException(AccessDeniedException ex) {
return new ResponseEntity<>(getBody(FORBIDDEN, ex, ex.getMessage()), new HttpHeaders(), FORBIDDEN);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> exception(Exception ex) {
return new ResponseEntity<>(getBody(INTERNAL_SERVER_ERROR, ex, "Something Went Wrong"), new HttpHeaders(), INTERNAL_SERVER_ERROR);
}
public Map<String, Object> getBody(HttpStatus status, Exception ex, String message) {
log.error(message, ex);
Map<String, Object> body = new LinkedHashMap<>();
body.put("message", message);
body.put("timestamp", new Date());
body.put("status", status.value());
body.put("error", status.getReasonPhrase());
body.put("exception", ex.toString());
Throwable cause = ex.getCause();
if (cause != null) {
body.put("exceptionCause", ex.getCause().toString());
}
return body;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With