Through the documentation of spring framework, I got to know about customizing exception handling mechanism of spring application. As per the documentation, It is stated that spring provides some default exception Resolvers.
SimpleMappingExceptionResolver
A mapping between exception class names and error view names. Useful for rendering error pages in a browser application.
DefaultHandlerExceptionResolver
Resolves exceptions raised by Spring MVC and maps them to HTTP status codes. See also alternative ResponseEntityExceptionHandler and REST API exceptions.
ResponseStatusExceptionResolver
Resolves exceptions with the #ResponseStatus annotation and maps them to HTTP status codes based on the value in the annotation.
ExceptionHandlerExceptionResolver
Resolves exceptions by invoking an #ExceptionHandler method in a #Controller or a #ControllerAdvice class. See #ExceptionHandler methods.
But in my requirement, I do not want the support of all these resolvers I just want ExceptionHandlerExceptionResolver to handle the exception in my Spring Application. So for that, I have added the following Configuration in my DispatcherServlet.
DispatcherServlet related configuration.
public void onStartup(ServletContext servletCxt) throws { ServletException {
......
dServlet.setDetectAllHandlerExceptionResolvers(false);
dServlet.setThrowExceptionIfNoHandlerFound(false);
.....
}
And in my Bean configuration java class, I have created following bean.
#Bean(name=DispatcherServlet.HANDLER_EXCEPTION_RESOLVER_BEAN_NAME)
HandlerExceptionResolver customExceptionResolver ( ) {
return new ExceptionHandlerExceptionResolver();
}
In the documentation, it is stated that
/**
* Set whether to detect all HandlerExceptionResolver beans in this servlet's context. Otherwise,
* just a single bean with name "handlerExceptionResolver" will be expected.
* Default is "true". Turn this off if you want this servlet to use a single
* HandlerExceptionResolver, despite multiple HandlerExceptionResolver beans being defined in the context.
*/
But even after all such configuration, I see HTML error pages and not JSON error response.
Sample ExceptionHandler method.
#ExceptionHandler(value={NullPointerException.class})
public ResponseEntity<Object> nullPointerExceptionHandler(NullPointerException e){
logger.info("local exception resolver running");
Map<String,Object> map=new HashMap<String,Object>(6);
map.put("isSuccess",false);
map.put("data",null);
map.put("status",HttpStatus.INTERNAL_SERVER_ERROR);
map.put("message", e.getMessage());
map.put("timestamp",new Date());
map.put("fielderror",null);
return new ResponseEntity<Object>(map,HttpStatus.BAD_GATEWAY);
}
What I am doing wrong? I only need the support of ExceptionHandlerExceptionResolver throughout my spring application to maintain consitency.
Related
I am trying to map exceptions from my rest controllers to responses which have a body, and to do it in a central place.
I have tried this:
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class RestErrorResponseExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleExceptionInternal(
Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
super.handleExceptionInternal(ex, body, headers, status, request);
return ResponseEntity.status(status).body(Error.from(status));
}
}
The problem is that the handler is never triggered.
If I define a custom method with #ExceptionHandler in my rest controllers, or extend something that has #ExceptionHandler, then all works well, but that introduces some bad design.
It is my understanding that Spring will first try to look in controller for exception handling methods, then it will check for registered handlers.
I am trying to verify the behaviour via WebMvcTest, and responses I'm getting are not the Error objects that I'm expecting.
Is there something I'm missing?
The ControllerAdvice is a configuration that have to be registered by Spring. You have to move your class in the config package or you can register it by annotation.
In my case, I work with a controllerAdvice like this one :
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler(MyException.class)
public ResponseEntity<String> reponseMyException(Exception e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("my message");
}
}
Spring Framework provides following ways to help us achieving robust exception handling.
Controller Based – We can define exception handler methods in our controller classes. All we need is to annotate these methods with #ExceptionHandler annotation. This annotation takes Exception class as argument. So if we have defined one of these for Exception class, then all the exceptions thrown by our request handler method will have handled.
These exception handler methods are just like other request handler methods and we can build error response and respond with different error page. We can also send JSON error response, that we will look later on in our example.
If there are multiple exception handler methods defined, then handler method that is closest to the Exception class is used. For example, if we have two handler methods defined for IOException and Exception and our request handler method throws IOException, then handler method for IOException will get executed.
Global Exception Handler – Exception Handling is a cross-cutting concern, it should be done for all the pointcuts in our application. We have already looked into Spring AOP and that’s why Spring provides #ControllerAdvice annotation that we can use with any class to define our global exception handler.
The handler methods in Global Controller Advice is same as Controller based exception handler methods and used when controller class is not able to handle the exception.
HandlerExceptionResolver – For generic exceptions, most of the times we serve static pages. Spring Framework provides HandlerExceptionResolver interface that we can implement to create global exception handler. The reason behind this additional way to define global exception handler is that Spring framework also provides default implementation classes that we can define in our spring bean configuration file to get spring framework exception handling benefits.
SimpleMappingExceptionResolver is the default implementation class, it allows us to configure exceptionMappings where we can specify which resource to use for a particular exception. We can also override it to create our own global handler with our application specific changes, such as logging of exception messages.
Make sure of 2 things and your code will work.
Your #ControllerAdvice class is available in component-scan path.
Make sure the methods in your #ControllerAdvice have structure somewhat like this-
#ExceptionHandler(value = { RequestProcessingException.class })
public #ResponseBody ResponseEntity<ErrorMessageBO> hotelConfigServiceExceptionHandler(HttpServletRequest request, RequestProcessingException e) {
logger.error("Exception with tracking Id: {}, dev message: {} and Message:", RequestContextKeeper.getContext().getRequestId(), e.getDeveloperMessage(),e);
return new ResponseEntity<ErrorMessageBO>(new ErrorMessageBO(e.getErrorCode(), e.getMessage(),RequestContextKeeper.getContext().getRequestId(),e.getDeveloperMessage()), HttpStatus.OK);
}
Is it possible to disable Spring Boot's autoexception handling from application.properties?
I am trying to disable Spring Boots general exception handling without renaming error.html while coding and turn it on at production from application.properties.
I've found the following from Spring documentation:
At start-up, Spring Boot tries to find a mapping for /error. By
convention, a URL ending in /error maps to a logical view of the same
name: error. In the demo application this view maps in turn to the
error.html Thymeleaf template. (If using JSP, it would map to
error.jsp according to the setup of your
InternalResourceViewResolver).
If no mapping from /error to a View can be found, Spring Boot defines
its own fall-back error page - the so-called “Whitelabel Error Page”
(a minimal page with just the HTTP status information and any error
details, such as the message from an uncaught exception). If you
rename the error.html template to, say, error2.html then restart, you
will see it being used.
And also I tried the following properties which didn't work:
server.error.path=/somewrongurl
server.error.whitelabel.enabled=false
Add following lines into your application.properties file
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
And define your own Exception Controller to catch all related exceptions like
#ControllerAdvice
public class ExceptionController {
#ExceptionHandler(value = Exception.class)
public ResponseEntity<?> handleException(Exception e) {
//handle your exception
}
}
Spring 3.2 introduced #ControllerAdvice annotation for handling exceptions in a Spring MVC application. But prior to this version Spring had #ExceptionHandler or HandlerExceptionResolver to handling exceptions in a Spring MVC application. Then why Spring 3.2 introduced #ControllerAdvice annotation for handling exceptions? I strongly believe that Spring 3.2 introduced #ControllerAdvice annotation to address the limitations of #ExceptionHandler or HandlerExceptionResolver or make the exceptions handling more stronger.
Can any one explain the advantages of #ControllerAdvice over #ExceptionHandler or HandlerExceptionResolver for handling exceptions?
#ExceptionHandler
#ExceptionHandler works at the Controller level and it is only active for that particular Controller, not globally for the entire application.
HandlerExceptionResolver
This will resolve any exception thrown by the application. It is used to resolve standard Spring exceptions to their corresponding HTTP Status Codes. It does not have control over the body of the response, means it does not set anything to the body of the Response.It does map the status code on the response but the body is null.
#ControllerAdvice
#ControllerAdvice used for global error handling in the Spring MVC application.It also has full control over the body of the response and the status code.
A #ExceptionHandler is local to a controller : only exceptions from this controller is routed to his #ExceptionHandler
But a #ControllerAdvice is global : you can have a centralized way to handle exceptions, binding, etc. it applies to all the defined controller.
Here is the difference:
If I need to configure the exception handling code then I need to use #ExceptionHandler annotation in my project which can be used in two diff.ways:
1)To use the annotation and handle the exception in the same controller locally in each controller class.
for eg:
#RestController
public class WSExposingController{
#GetMapping("/getAllDetails/{/id}")
public UserDetails myMethod(#PathVariable int id){
UserDetails user = UserDetailsService.getUser(id);
return user;
}
//To handle the exceptions which are resulting from this controller, I can declare an exceptionHandler specifically in the same class
#ExceptionHandler(Exception.class)
public ResponseEntity handlerSameControllerExceptions(){
return new ResponseEntity(null,HttpStatus.INTERNAL_SERVER_ERROR);
}
}
2)If I create a new class which extends
ResponseEntityExceptionHandler(SpringBoot class) and if I ANNOTATE IT WITH
#ControllerAdvice then that class becomes
globalexceptionhandler meaning to say that any exception which is resulting in any controller class can be handled here. And it can be present in any package
in the same project.
#RestController
#ControllerAdvice
public class GlobalJavaExceptionHandler extends ResponseEntityExceptionHandler{
#ExceptionHandler(Exception.class)
public ResponseEntity handleDiffControllerExceptions(){
return new ResponseEntity(null,HttpStatus.INTERNAL_SERVER_ERROR);
}
If both are present in the code then the local with take precedence over the global one.
Ideally the second option is a better one as we need not add the code in each and every controller class and this class with #ControllerAdvice can be a one stop solution for all the exceptions arising due to the code beginning from the controller to the dao throughout the length of the code.
#ExceptionHandler can be used at the local level or at the global level.
Local level would mean using this annotation within the controller itself to handle the exceptions within that controller only. All error thrown by that controller would be caught by that #ExceptionHandler.
But this would mean that if there is a similar exception in a different controller you would have to rewrite the corresponding code again in that controller again locally.
In order to prevent repeating this style of exception handling per controller we can write the #ExceptionHanlder at the global level with the help of another annotation called #ControllerAdvice.
#ControllerAdvice is not specific to the exception handling , its also used for handling property, validation or formatter bindings at the global level.
#ControllerAdvice in the context of exception handling is just another way of doing exception handling at a global level using #Exceptionhandler annotation.
Now coming to the HandlerExceptionResolver - this is an interface at a more lower level. Spring provides 2 implementations of this:
ResponseStatusExceptionResolver :This supports the support the #ResponseStatus annotation
ExceptionHandlerExceptionResolver : This supports the #ExceptionHandler annotation
Example : So when you want to handle exceptions and choose an exception handling strategy you will need to think of choosing between using a local or global exception handling via the annotations. How you need to provide the HTTP status codes, how to wrap it in the #Response entity etc, how you want to redirect to handler pages , carry the data via flash attributes or get params etc etc.
Or maybe skip the annotations and use the SimpleMappingExceptionResolver and start mapping the specific exceptions to the error handler page urls
Here we are not be considering the lower level underlying HandlerExceptionResolver at this stage since we are dealing with its implementation at a higher level and building the strategy based on these options.
With the above context to answer your query - #ControllerAdvice was not introduced for exception handling, it's a mechanism you can leverage to handle exceptions globally using the #ExceptionHandler. HandlerExceptionResolver is an interface whose implementation helps support the #ResponseStatus and the #Exceptionhandler annotations. Unless you want to handle the exceptions related to the MVC system because the Spring framework does not provide any proper exception handling. So if you need to hand;e issues related to incorrect #Requestmapping etc which would not be caught by a controller as it would not even reach it in the 1st place then an implementation of the HandlerExceptionResolver would be useful
I am currently implementing Spring Security OAuth2 and would like to provide my own custom error page when an OAuth2Exception is thrown. Currently there is a Framework endpoint in WhitelabelErrorEndpoint.java that is mapped to /oauth/error, which provides the default error handling. The reference docs claim:
All you need to do is provide a Spring MVC controller with #RequestMappings for those endpoints, and the framework defaults will take a lower priority in the dispatcher.
However, in practice I am not seeing my Spring controller taking priority over the Whitelabel default. What am I doing wrong?
Example Controller:
#Controller
#Scope(WebApplicationContext.SCOPE_REQUEST)
public class OAuthExceptionController {
private static final Logger logger = LoggerFactory.getLogger(OAuthExceptionController.class);
#RequestMapping("/oauth/error")
public ModelAndView defaultOAuthErrorHandler(HttpServletRequest request) throws Exception {
logger.error("Oauth2 exception occurred");
Map<String, Object> model = Maps.newLinkedHashMap();
model.put("path", request.getServletPath());
return new ModelAndView("error/accessDenied", model);
}
}
I am trying to return a valid JSON body for every request of an Spring MVC service, both correct and invalid.
Using #ExceptionHandler and a combination of ExceptionHandlerExceptionResolver, DefaultHandlerExceptionResolver and my own AbstractHandlerExceptionResolver I am able to process and set a body for almost all invalid requests that are dispatched to a valid controller or rejected by Spring Security.
However, errors thrown by the DispatcherServlet like noHandlerFound, can't be intercepted, so the response has a valid HTTP status code like 404, but the body contains the Tomcat default XML error.
Anybody knows how to set up or configure an Spring Exception Resolver so all failed requests are intercepted and a valid JSON body could be set?
There is a section explaining how is spring handling exception resolver for dispatcher servlet:
The dispatcher's exception resolution strategy can be specified via a
HandlerExceptionResolver, for example mapping certain exceptions to
error pages. Default are AnnotationMethodHandlerExceptionResolver,
ResponseStatusExceptionResolver, and DefaultHandlerExceptionResolver.
These HandlerExceptionResolvers can be overridden through the
application context. HandlerExceptionResolver can be given any bean
name (they are tested by type).
to be able to support Dispatcher exception handling overriding you have to implement HandlerExceptionResolver and Ordered class and declare the implementation as spring bean. You must return getOrder value as Integer.MIN_VALUE to override any other existing handler.