How does spring handle exceptions in MVC - spring

Spring MVC exception handle
In my opinion there are different kinds of exceptions in a Spring MVC application.
For example, the service or dao layer may throw exceptions once errors occured. Or the spring itself may throw exceptions if it can not find a right handler to handle the request.
And now I try to use the ControllerAdvice to handle the exceptions:
#ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
#ExceptionHandler(Exception.class)
public ModelAndView myError(Exception exception) {
log.error(exception.getMessage());
ModelAndView mav = new ModelAndView();
mav.addObject("exception", exception);
mav.setViewName("error");
return mav;
}
}
However once I visit a wrong page, I will get the 404 error page from tomcat, rather than the error view defined in the ControllerAdvice.
So how did spring handle the exceptions?

I Think you rather put the error page in Web.xml, whenever you will visit the wrong page it will be redirected to this error page. Also this is less cumbersome and easy to manage.
Does this answers your question

Related

#ControllerAdvice handler not being called from Spring Cloud custom filter

I have created the following custom filter to be used for authorization in a Spring Cloud application. Here is the apply() method from that filter, from which an exception is thrown should the authorization check fail:
#Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
HttpStatus status = HttpStatus.FORBIDDEN;
try {
String authRequest = exchange.getRequest().getHeaders().getFirst(
Constants.SESSION_HEADER_NAME);
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> entity = new HttpEntity<String>(authRequest, headers);
// make REST call to authorization module
status = restTemplate.postForEntity(authURL, entity, String.class).getStatusCode();
}
catch (Exception e) {
LOGGER.error("Something went wrong during authorization", e);
}
// throw an exception if anything went
// wrong with authorization
if (!HttpStatus.OK.equals(status)) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
}
return chain.filter(exchange);
};
}
I have defined the following #ControllerAdvice class to catch all exceptions thrown by the above Gateway Cloud filter:
#ControllerAdvice
public class RestExceptionHandler {
#ExceptionHandler(value = ResponseStatusException.class)
public final ResponseEntity<Object> handleException(ResponseStatusException ex) {
return new ResponseEntity<>("UNAUTHORIZED", HttpStatus.FORBIDDEN);
}
}
What I currently observe is the following:
The above custom filter's ResponseStatusException is not being caught by the #ControllerAdvice mapping method.
Yet, throwing this exception from elsewhere in the Spring Boot application, e.g. the regular controller we use as an authentication endpoint, is caught by the #ControllerAdvice method.
For now, this is more of a nuisance than a blocker, because in fact throwing a ResponseStatusException from the Cloud filter with a custom error code in fact does return that error code to the caller. But, it would be nice to handle all exceptions in a single place.
Can anyone shed some light on this problem?
From ControllerAdvice javadocs:
Classes with #ControllerAdvice can be declared explicitly as Spring beans or auto-detected via classpath scanning.
You didn't show full class for your filter, but I bet it isn't Spring Bean scanned on classpath. Typically servlet filters are explicitly plugged into Spring Security configuration. Therefore ControllerAdvice processing is ignoring it.
I assume that by Filter you mean javax.servlet.Filter.
In that case, #ControllerAdvice cannot work. It is used to handle exceptions from Controllers. But you throw Exception before it can even propagate to Controller (by not calling the chain.filter(exchange) method.
Try throwing exceptions in controller, not in filter.
Edit: If you don't want to handle exceptions on #Controller, you must implement terminal handler in javax.servlet.Filter directly. That means change the incoming request' response directly like this:
HttpServletResponse httpResponse = (HttpServletResponse) exchange.getResponse();
// either format the response by yourself
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.setHeader(...)
...
// or let populate error directly
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
... which is something what #ControllerAdvice does internally.

How exactly work the HandlerExceptionResolver interface in Spring? Can I show an error page when the user try to access to a not mapped resource?

I am studying how to handle custom exception in Spring on a tutorial that show me this class named ExceptionHandler that implement the HandlerExceptionResolver Spring interface:
#Component
public class ExceptionHandler implements HandlerExceptionResolver {
private static final Logger logger = LoggerFactory.getLogger(ExceptionHandler.class);
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object object, Exception exception) {
System.out.println("Spring MVC Exception Handling");
logger.error("Error: ", exception);
return new ModelAndView("error/exception","exception","ExceptionHandler message: " + exception.toString());
}
}
And then, into a controller class of the example, it throws this exception in this way:
#RequestMapping(value="/throwRunTimeException", method=RequestMethod.GET)
public void throwException() {
throw new RuntimeException();
}
So I have some doubts about how exactly do this class.
I can't understand if implementing the HandlerExceptionResolver interface I am declaring a new specific exception type or if simply specify a specific behavior that happens when a generic runtime exception is thrown.
It seems me the second situation...I think that, in the previous example, when a generic RuntimeException is thrown it return an exception.jsp page (and the related message that have to be shown into the model object).
So, if the previous assertion is true, can I use this method for the following pourpose?
I think that when a user try to open an URL that is it not mapped to any controller method a RuntimeException is thrown.
So, into the **resolveException()** method, can I extract the required URL from the HttpServletRequest request input parameter and use it to show a specific error message (that indicate that this URL not exist) into the returned view?
I don't think that is possible. When the DispatcherServlet can't find the url mapped in one of your controllers, it will throw a NoHandlerFoundException. This will then be forwarded to your servlet container like Tomcat which handles the error and shows the 404 page for example. You can change this behaviour by adding the following to your web.xml:
`
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/404error.jsp</location>
</error-page>
`
Note that it's not possible yet to configure this in JavaConfig.
For other Exceptions that are thrown you can use the HandlerExceptionResolver to return the desired view.
You could also use the #ExceptionHandler annotation on a method in your controller to catch the exceptions and handle them appropriately. This can be combined with the #ControllerAdvice annotation to enable this for every controller.

Spring MVC handling exceptions

I've built a spring mvc application using the controller->service->dao architecture. The DAO objects are using hibernate. The services are annotated #Transactional.
I'm trying to catch dao exceptions in the service, wrap them up and then throw them to my controller:
Service
#Override
public Entity createEntity(Entity ent) throws ServiceException {
try {
return entityDAO.createEntity(ent);
} catch (DataAccessException dae) {
LOG.error("Unable to create entity", dae);
throw new ServiceException("We were unable to create the entity for the moment. Please try again later.", dae);
}
}
Controller
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String createEntity(#ModelAttribute(value = "newEntity") Entity newEntity, RedirectAttributes redirectAttributes) {
try {
entityService.createEntity(newEntity);
} catch (ServiceException se) {
redirectAttributes.addFlashAttribute("error", se.getMessage());
}
}
return "redirect:/entity/manage";
}
However, even though the DataAccessException is caught at the service level, it keeps bubbling up to my controller somehow.
If for example I don't meet a unique field criteria on the database level I get an HTTP Error 500 with the following:
org.hibernate.AssertionFailure: null id in com.garmin.pto.domain.Entity entry (don't flush the Session after an exception occurs)
Code is caching DataAccessException not HibernateException, try caching HibernateException
Is there a way to translate HibernateException to something else, then DataAccessException in sping
If you want to handle the exception in the Controller, don't catch it in the Service.
Service
#Override
public Entity createEntity(Entity ent) throws DataAccessException {
return entityDAO.createEntity(ent);
}
Controller
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String createEntity(#ModelAttribute(value = "newEntity") Entity newEntity, RedirectAttributes redirectAttributes) {
try {
entityService.createEntity(newEntity);
} catch (DataAccessException e) {
redirectAttributes.addFlashAttribute("error", e.getMessage());
}
return "redirect:/entity/manage";
}
Or if you want to leverage Spring the handle the exception, use ExceptionHandler annotation. You can find good tutorial online, for example, Spring MVC #ExceptionHandler Example.
To make exception translation working
You have to annotate your DAO with #Repository
Make sure you have declared this bean <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
Here is a beautiful post on different ways to handle exceptions on a Spring MVC project.
Among those, I find using #ControllerAdvice classes, to handle all the exceptions at one place globally, the most convenient in general. Spring Lemon's source code could serve as a good concrete example.

Spring REST API custom exceptions handling

I have a Spring 3.2 application and I've created a REST API based on Spring MVC. I am using #ControllerAdvice annotation for custom exceptions handling. For example:
#ControllerAdvice
public class RestResponseEntityExceptionHandler {
#ExceptionHandler(MyCustomException.class)
#ResponseStatus(HttpStatus.CONFLICT)
#ResponseBody
public ExceptionMessage handleMyCustomException(MyCustomException ex){
return new ExceptionMessage(ex.getClass().getName(), ex.getMessage(), ex.getExceptionCode());
}
}
The issue is that I see how my custom exception is thrown but the exception handler method is actually not being executed and hence my exception message is not returned to the client. Instead, I noticed in the logs how the DefaultHandlerExceptionResolver handles the exception (with a Spring generic one, ServletRequestBindingException in a GET method). How can I get rid of this issue?
Thanks!
The ServletRequestBindingException is a hint that something went wrong before the handler method of the controller. In this case some binding issue.
Exception handlers annotated with #ExceptionHandler are only called when an exception is thrown within a controller handler method (#RequestMapping).

Handling addtional exceptions and throwing custom error message in Spring security

I am using Spring 2.5 .I want to do the following:
In the overriden loadUserByUsername method of spring UserDetailsService I want to throw a custom exception and on that exception give a custom error message on the login page.
Could anyone suggest how can I handle this custom exception.
You can extend AbstractHandlerExceptionResolver and handle your exception in this class. Code is not full, but you can understand the idea from that part.
public class ExceptionResolver extends AbstractHandlerExceptionResolver {
#Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mav;
if(ex instanceof YourException){
mav = loginController.showLoginPage();
mav.addObject("errorMessage","Some error message text.");
\\also you can change response code here or add some logic
}
else {
\\some another page
}
return mav;
}
Also add this to applicationContext.xml(or another file with your spring context configuration):
<bean class="com.mycompany.ExceptionResolver" />

Resources