Handle custom exceptions using #ExceptionHandler - java

I have a class that catches exceptions using the Spring annotation #ExceptionHandler and this class support some custom exceptions as well. I would like that when I raise the exception CustomRuntimeException in this way throw new CustomRuntimeException(args...);, it will be caught and handled in the following method:
#ControllerAdvice
public class CutomExceptionManager {
// code
#ExceptionHandler({CustomRuntimeException.class})
#ResponseBody
private ResponseEntity<ErrorResource> handleCustomException(CustomException e, HttpServletRequest request, HttpServletResponse response) {
logger.error("unhandled exception: ", (Exception)e);
// other code
}
}
This doesn't work.

As mentioned in the comments by #Tom Elias, the method should be protected or public to be taken in account by Spring.
As an example, the following code is working.
#ExceptionHandler(ControllerException.class)
public ResponseEntity<ControllerException> handleControllerException(ControllerException controllerException) {
log.error(controllerException.getFullMessage(), controllerException);
return new ResponseEntity<>(controllerException, HttpStatus.valueOf(controllerException.getStatus()));
}
BTW, no need to add the #ResponseBody annotation to the method.

Related

How to use #ControllerAdvice for catching exceptions from Service classes?

I have some Service classes which contain multiple methods that throws error, an example of methods that throws an error:
public Optional<Item> getItemById(Long itemId) throws Exception {
return Optional.of(itemRepository.findById(itemId).
orElseThrow(() -> new Exception("Item with that id doesn't exist")));
}
Should I catch errors in the #ControllerAdvice annoted class?
How should I do it?
The controller marked with #ControllerAdvice will intercept any exception thrown in the stack called when a request arrives. If the question is if you should catch errors with ControllerAdvice, is up to you, but it allows you to customize the behaviour once a exception is thrown. To do it you should create a class like this:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler({ Exception.class, MyCustomException.class }) //Which exceptions should this method intercept
public final ResponseEntity<ApiError> handleException(Exception ex){
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND); //Or any HTTP error you want to return
}
}

How to handle Exception in controller for Spring Boot REST API?

I am confused of how I should handler the exception of the controller in a Spring Boot Rest API. Right now I throw some exception in my service classes like this:
public Optional<Item> getSpecificItem(Long itemId) throws Exception {
return Optional.ofNullable(itemRepository.findById(itemId).
orElseThrow(() -> new Exception("Item with that id doesn't exist")));
}
I don't know if this is the correct way to do it but it kind of works, I am open to criticism. For the controller classes I don't know how it should look, I saw some example with #ControllerAdvice and exception for each controller and that looked kind of bad to me. Can I have a global exception class for all controllers? Is it good practice ?
Saw some examples and I don't know if they were the correct way to do it.
#ControllerAdvice is good if you not use for general Exception. Example, if you define spec exception such as SkyIsRedException. So, it will be throw anywhere it will catch.
#ControllerAdvice
public class ExampleAdvice {
#ExceptionHandler(SkyIsRedException.class)
#ResponseCode(HttpStatus.NOT_FOUND) // <- not required
public void methodName1() { ... }
#ExceptionHandler(SkyIsGreenException.class)
public void methodName2() { ... }
}
And you can this #ExceptionHandler in controller too, so it will activate if any methods of controller will throw this SkyIsRedException.
I not recommend use Exception for everything. You are only harming yourself.
UPDATE:
// annotations
public class Controller {
// annotations
public Optional<Item> getSpecificItem(Long itemId) throws ItemNotExistException {
return Optional.ofNullable(itemRepository.findById(itemId).
orElseThrow(() -> new ItemNotExistException("Item with that id doesn't exist")));
}
// Controller specific exception handler, not central like #ControllerAdvice
#ExceptionHandler(ItemNotExistException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String itemNotExistExceptionHandler(ItemNotExistException ex) {
return ex.getMessage(); // example
{
}

#ControllerAdvice method is not getting executed and it executes Base Response Class

I wrote my Spring Boot ProductController Class with productDetail method & handleMethodArgumentNotValid method . handleMethodArgumentNotValid method is annotated with #ExceptionHandler(MethodArgumentNotValid.class). It worked perfectly fine. After that I removed
handleMethodArgumentNotValid method from Controller class, as I would like to use #ControllerAdvice. But it is executing BaseException class of the project. It is not executing #ControllerAdvice method.
Here is my Controller class.
#PostMapping("/productDetail")
public void productDetail(#Valid #RequestBody ProductDetail productDetail) {
System.out.println("I am in Controller ProductDetail ....");
try {
iOrderService.updateProductDetail(productDetail);
} catch (Exception e) {
//Executes Base Exception class information here
...
}
}
Here is my ControllerAdvice .
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#Override
#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request
) {
same code that I had in handleMethodArgumentNotValid method of ProductController class here
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.UNPROCESSABLE_ENTITY.value(),
"Validation error. Check 'errors' field for details."
);
for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) {
errorResponse.addValidationError(fieldError.getField(),
fieldError.getDefaultMessage());
}
return ResponseEntity.unprocessableEntity().body(errorResponse);
}
How can I handle MethodArgumentNotValidException so that it won't execute BaseException class?
Your global exception handler can only handle uncaught exceptions. So if you want it to handle anything thrown by iOrderService.updateProductDetail(productDetail);, you'll need to remove the try/catch.
I suspect that your test input to productDetail() is not actually causing a MethodArgumentNotValidException. Either that or your global exception handler is not included in your component scan. I'd recommend adding a "catchAll" method to your global exception handler for testing purposes. Just to see if it's catching any exceptions at all.
#ExceptionHandler(Exception.class)
protected ResponseEntity<ExceptionEnvelope> catchAll(Exception exception, WebRequest request) {
return buildResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception, request);
}
Set a breakpoint in there and just see if you're able to hit it. I've had issues like this before and it ended up being that my assumptions were incorrect about which exceptions spring throws for different circumstances. Catching all exceptions like this will allow you to validate that the GlobalExceptionHandler is wired up properly, and will also tell you which exception is actually getting thrown.
I changed RestController annotation to #Controller annotation in Controller Class and annotated method with #ResponseBody and it worked.

Spring REST #ResponseStatus with Custom exception class does not change the return Status code

I have a exception class like follows
#ResponseStatus(value=HttpStatus.UNPROCESSABLE_ENTITY, reason="Unprocessable Entity") // 422
public class UnprocessableEntityException extends RuntimeException {
}
Now the status is not returned as 422 unless I write a specific handler in the Controller class like :
#ExceptionHandler(UnprocessableEntityException.class)
#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public String handleException(Exception ex) {
...
}
As I understand I should not need #ExceptionHandler in first place, not sure what am I missing.
Throwing a #ResponseStatus annotated exception from a controller method should be enough for the framework to write the HTTP status code - no #ExceptionHandler necessary.
The following will write a 422 Status on hitting the webapp root as expected:
#Controller
public class ExceptionController {
#RequestMapping("/")
public void action() {
throw new ActionException();
}
#ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY, reason = "nope")
public static class ActionException extends RuntimeException {}
}
This works courtesy of the ResponseStatusExceptionResolver which is created by Spring MVC by default - if it's not working for you, my guess is that this default exception resolver has been removed (by e.g. overriding WebMvcConfigurationSupport.configureHandlerExceptionResolvers or otherwise configuring your context's HandlerExceptionResolvers so that the ResponseStatusExceptionResolver is trumped.)
The exception thrown should not be handled by code or by other exception resolvers, for example it shouldn't be handled by #ExceptionHandler, because that will override the status code specified by the exception class's #ResponseStatus.

Why #ExceptionHandler(MethodArgumentNotValidException.class) is ignored in favour of #ExceptionHandler(Exception.class)

I know that Exception is the Parent of all exceptions but I thought when you set #ExceptionHandler with specific exception class this should handle that specific exception.
Maybe you can point what I have missed in following code so MethodArgumentNotValidException will go into processValidationError method not processError method.
#ControllerAdvice
public class ExceptionHandler {
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public ValidationErrorDTO processError(Exception e) {
return processErrors(e);
}
}
#ControllerAdvice
public class OtherExceptionHandler {
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ValidationErrorDTO processValidationError(MethodArgumentNotValidException ex) {
return processErrors(ex);
}
}
After your edit it's clear that you have more than one #ControllerAdvice class.
In short, the problem is that your ExceptionHandler class (and its #ExceptionHandler for Exception.class) gets registered first by Spring, and because Exception handler matches any exception, it will be matched before Spring ever gets to more specific handlers defined.
You can read detailed explanation in #Sotirios answer here.
I'd recommend you register only one ControllerAdvice and to make sure it extends ResponseEntityExceptionHandler, so the default handling of MethodArgumentNotValidException is not overwritten.
If you then wish to alter the logic of handling the MethodArumentNotValidException, you can override the handleMethodArgumentNotValid method.

Categories

Resources