#RestControllerAdvice and #ControllerAdvice together - spring

I have an Spring MVC application which has #Controller s and #RestController s.
I was thinking that: When I have some Exception at my #Controller, It gonna be handled by my #ControllerAdvice and when I have some Exception at my #RestController, It gonna be handled by my #RestControllerAdvice... But now I think It's not how things should work, because my #ControllerAdvice are catching everything, even any exception that is thrown by #RestController...I do not know if this should happen. Here my code:
#ControllerAdvice
public class ExceptionHandlerController {
private final String DEFAULT_ERROR_VIEW = "error/default";
#ExceptionHandler(Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e)
{
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("danger", e.getMessage());
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
#RestControllerAdvice
public class ExceptionHandlerRestController {
#ExceptionHandler(Exception.class)
public ResponseEntity<String> defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
return new ResponseEntity<>(" test "+e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}

Yeah #RestControllerAdvice doesn't work that way. It's just a #ControllerAdvice with #ResponseBody automatically assumed. See #RestControllerAdvice vs #ControllerAdvice.
If you wanted one #ControllerAdvice to work with one controller and one to work with the other then if you put your controllers in separate packages you should be able to do this by doing:
#ControllerAdvice("my.controller1.package")
However, the whole point of #ControllerAdvice is to share common functionality of your separate #Controllers between each other so if you want them to do separate things you might be better off just putting the methods inside the #Controllers themselves.

If you want #RestControllerAdvice to handle only exceptions thrown from #RestController, then you can qualify it with the annotations attribute:
#RestControllerAdvice(annotations = RestController.class)
You may need #Order tag if you happen to have several other #ControllerAdvice.

Related

Generic exception handler prevents specific exception being handled

I have a spring boot application and there I handle exceptions. I have two exception handler methods for now:
#ControllerAdvice
#RestController
public class MyExceptionHandler {
#ExceptionHandler(Exception.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public final BaseResponse<GenericException> handleGenericExceptions(Exception ex, HttpServletRequest request) {
....
return response;
}
#ExceptionHandler(MyCustomException.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public final BaseResponse<MyCustomException> handleMyCustomException(MyCustomException ex, HttpServletRequest request) {
....
return response;
}
}
At some point, I throw a MyCustomException exception, but when I debug I see that it is handled by handleGenericExceptions method. If I delete handleGenericExceptions method, then it is handled by handleMyCustomException message.
I want MyCustomException to be handled by handleMyCustomException method, and all other exceptions by handleGenericExceptions method. MyCustomException class extends Throwable. What is wrong here?
Thanks.
I found the solution, but I do not now why this is the case.
MyCustomException class was extending Throwable, I changed it to extend RunTimeException instead. Now it is working as expected.
I don't have 50 repuatations,so I answered here, hope it make sense.
Throwable class have two important subclass ,Error and Exception . your MyCustomException extend Throwable. In spring , if your code throws which extends Throwable , Spring defaultly will decorate it as an IllegalStateException! I see this in spring4.3 source code , if you want to prove this , you add #ExceptionHandler({IllegalStateException}) and make your MyCustomException unmodified , this method will catch it!
Above evidence is in org.springframework.web.method.support.InvocableHandlerMethod#doInvoke.
By the way , we usually throw Exception by extends RuntimeException , why you want to throw an Throwable .

Spring MVC - ExceptionHandler

I am using Spring Boot 1.5.7.
I have an ExceptionHandler for my exception that returns a ResponseEntity
#ExceptionHandler(MyException.class)
public ResponseEntity<ResponseExceptionEntity> handleException(MyException e) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ResponseExceptionEntity(e));
}
This works well in situations where the exception occurs during an api call returning a ResponseEntity / #ResponseBody (JSON/XML response)
I would like to return a ModelAndView in situations where the exception occurs during a request where HTML is returned. In my situation all Controllers are annotated with #Controller and not #RestController
how can I write a ExceptionHandler for both cases (api/html)?
Or How can I determine what the resolved view is for a controller method?
Or how can I determine what the return type of the Controller Method
is?
Or How can I determine what the resolved view is for the controller
method?
I've tried the suggestion in this answer, but it doesn't return a JSON response when the Controller Method is annotated with #ResponseBody.
https://stackoverflow.com/a/32071252/461055
Yes, you can write two exception handler to handle api and html request exception. Here is sample code to illustrate the solution.
#ControllerAdvice(annotations = RestController.class)
#Order(1)
class RestExceptionHandler {
#ExceptionHandler(MyException.class)
#ResponseBody
ResponseEntity<ErrorResponse> exceptionHandler() {
....
}
}
#ControllerAdvice(annotations = Controller.class)
#Order(2)
class ExceptionHandler {
#ExceptionHandler(Exception.class)
public ModelAndView handleError500(HttpServletRequest request, HttpServletResponse response, Exception ex) {
ModelAndView mav = new ModelAndView("error");
mav.addObject("error", "500");
return mav;
}
}

Does ControllerAdvice increase response time?

Is there any speed difference when using ControllerAdvice throwing RuntimeException, and when manually returning ResponseEntity to handle client errors?
1) ControllerAdvice
#RestController
public class ObjectController {
#PostMapping
public Object save(#RequestBody Object object) {
if (service.isInvalid(object))
throw new ObjectException("Client error");
return service.save(object);
}
}
public class ObjectException extends RuntimeException {
}
#ControllerAdvice
public class ObjectControllerAdvice extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {ObjectException.class})
protected ResponseEntity<Object> handleConflict(ObjectException ex, WebRequest request) {
return handleExceptionInternal(ex, ex.getLocalizedMessage(), new HttpHeaders(),
HttpStatus.BAD_REQUEST, request);
}
}
2) Manually returning ResponseEntity
#RestController
public class ObjectController {
#PostMapping
public ResponseEntity<Object> save(#RequestBody Object object) {
if (service.isInvalid(object))
return new ResponseEntity<>("Client error", HttpStatus.BAD_REQUEST);
return new ResponseEntity<Object>(service.save(object), HttpStatus.OK);
}
}
I imagine the difference is response time is negligible with the second approach possibly being very slightly faster. But the real advantage of having a #ControllerAdvice class with #ExceptionHandlers is that these can be used for multiple endpoints over multiple #Controllers and you won't have to repeat the code everywhere.
No, it's not that much different. And I think using the #ControllerAdvice is a best practice when you would like to handle your Custom Exception or to centralize the Exception to a Global class. There is a simple sample in this answer: Error page registrar and Global exception handling
Hope this help.

#RestControllerAdvice vs #ControllerAdvice

What are the major difference between #RestControllerAdvice and #ControllerAdvice ??
Is it we should always use #RestControllerAdvice for rest services and #ControllerAdvice MVC ?
#RestControllerAdvice is just a syntactic sugar for #ControllerAdvice + #ResponseBody, you can look here.
Is it we should always use #RestControllerAdvice for rest services and
#ControllerAdvice MVC?
Again, as mentioned above, #ControllerAdvice can be used even for REST web services as well, but you need to additionally use #ResponseBody.
In addition, we can just understand it as:
#RestControler = #Controller + #ResponseBody
#RestControllerAdvice = #ControllerAdvice + #ResponseBody.
Keeping in mind that #RestControllerAdvice is more convenient annotation for handling Exception with RestfulApi.
Example os usage:
#RestControllerAdvice
public class WebRestControllerAdvice {
#ExceptionHandler(CustomNotFoundException.class)
public ResponseMsg handleNotFoundException(CustomNotFoundException ex) {
ResponseMsg responseMsg = new ResponseMsg(ex.getMessage());
return responseMsg;
}
}
In that case any exception instanceOf CustomNotFoundException will be thrown in body of response.
Example extracted here:
https://grokonez.com/spring-framework/spring-mvc/use-restcontrolleradvice-new-features-spring-framework-4-3
Exception: A good REST API should handle the exception properly and send the proper response to the user. The user should not be rendered with any unhandled exception.
A REST API developer will have two requirements related to error handling.
Common place for Error handling
Similar Error Response body with a proper HTTP status code across APIs
#RestControllerAdvice is the combination of both #ControllerAdvice and #ResponseBody
The #ControllerAdvice annotation was first introduced in Spring 3.2.
We can use the #ControllerAdvice annotation for handling exceptions in the RESTful Services but we need to add #ResponseBody separately.
Note:
GlobalExceptionHandler was annotated with #ControllerAdvice, thus it is going to intercept exceptions from controllers accross the application.
The differences between #RestControllerAdvice and #ControllerAdvice is :
#RestControllerAdvice = #ControllerAdvice + #ResponseBody. - we can
use in REST web services.
#ControllerAdvice - We can use in both MVC and Rest web services, need to
provide the ResponseBody if we use this in Rest web services.
For Example :
Exception Class:
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends Exception{
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message){
super(message);
}
}
usage of the above exception in Rest Web Service.
#RestControllerAdvice
public class MyRestControllerAdviceHandler {
#ExceptionHandler(ResourceNotFoundException.class)
public ResponseMsg resourceNotFoundException(ResourceNotFoundException ex) {
ResponseMsg resMsg = new ResponseMsg(ex.getMessage());
return resMsg;
}
}
usage of the above exception in MVC.
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
If you use #ControllerAdvice and return your error object from a method then it will look for a view with the name of your error object so instead of returning the expected response it will return 404 for not founding a view page with that name
#ControllerAdvice
public class CustomizedExceptionHandler {
#ExceptionHandler({ UserNotFoundException.class })
#ResponseStatus(code = HttpStatus.BAD_REQUEST)
public ExceptionResponce handleUserNotException(Exception ex, WebRequest request) throws Exception {
ExceptionResponce exceptionResponce = new ExceptionResponce(new Date(), ex.getMessage(),
request.getDescription(false));
return exceptionResponce;
}
}
As in the above code, I want to return 400 (BAD_REQUEST) but
instead of 400, it is returning 404(NOT_FOUND)
You can solve this issue by using any of the below ways
add #ResponseBody to your method or class.
Use #RestControllerAdvice.
Or you can wrap your error object in ResponseEntity.
After using either of the above ways it returns the correct response

Spring 4 mvc global exception Handling

I'm very new to spring mvc sorry if I'm asking a basic questions, I need to maintain Global Exception Handling in my spring 4 MVC, Jersey project and return JSON response to IOS mobile app. Now by using #ControllerAdvice and #ExceptionHandler, I have created a class like below
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ExceptionHandler(Exception.class)
public #ResponseBody handleException(HttpServletRequest reqException ex) {
ErrorInfo response=new ErrorInfo();
if(ex.getMessage.contains("java.io")){
response.setMessage("FileNotFound exception occur");
return response;
}else if ...
}
Please advice if above approach is correct or is there any alternative way to handle all exceptions occur in controller,service and DAO layer.
what you use is correct, all exceptions just be handled.In service or Dao layer,you just need to throw your business exception.The class you have created will catch the exception.But you should handle the exception in different ways,and define your own business exception.
here are some example codes.
#ExceptionHandler(RuntimeException.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public ErrorResponse handleUnexpectedServerError(RuntimeException ex) {
ex.printStackTrace();
return new ErrorResponse("012", ex.getMessage());
}
handle the business exception,the BusinessErrorException is custom exception.
#ExceptionHandler(BusinessErrorException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public ErrorResponse handleBusinessErrorException(BusinessErrorExceptionex) {
return new ErrorResponse(ex.getCode(), ex.getMessage());
}

Resources