Handle error 404 with Spring controller - java

I use #ExceptionHandler to handle exceptions thrown by my web app, in my case my app returns JSON response with HTTP status for error responses to the client.
However, I am trying to figure out how to handle error 404 to return a similar JSON response like with the one handled by #ExceptionHandler
Update:
I mean, when a URL that does not exist is accessed

I use spring 4.0 and java configuration. My working code is:
#ControllerAdvice
public class MyExceptionController {
#ExceptionHandler(NoHandlerFoundException.class)
public ModelAndView handleError404(HttpServletRequest request, Exception e) {
ModelAndView mav = new ModelAndView("/404");
mav.addObject("exception", e);
//mav.addObject("errorcode", "404");
return mav;
}
}
In JSP:
<div class="http-error-container">
<h1>HTTP Status 404 - Page Not Found</h1>
<p class="message-text">The page you requested is not available. You might try returning to the home page.</p>
</div>
For Init param config:
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
}
Or via xml:
<servlet>
<servlet-name>rest-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
See Also: Spring MVC Spring Security and Error Handling

With spring > 3.0 use #ResponseStatus
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
...
}
#Controller
public class MyController {
#RequestMapping.....
public void handleCall() {
if (isFound()) {
// do some stuff
}
else {
throw new ResourceNotFoundException();
}
}
}

Simplest way to find out is use the following:
#ExceptionHandler(Throwable.class)
public String handleAnyException(Throwable ex, HttpServletRequest request) {
return ClassUtils.getShortName(ex.getClass());
}
If the URL is within the scope of DispatcherServlet then any 404 caused by mistyping or anything else will be caught by this method but if the URL typed is beyond the URL mapping of the DispatcherServlet then you have to either use:
<error-page>
<exception-type>404</exception-type>
<location>/404error.html</location>
</error-page>
or
Provide "/" mapping to your DispatcherServlet mapping URL so as to handle all the mappings for the particular server instance.

public final class ResourceNotFoundException extends RuntimeException {
}
#ControllerAdvice
public class AppExceptionHandler {
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String handleNotFound() {
return "404";
}
}
Just define an Exception, an ExceptionHandler, throw the Exception from your business code controller.

You can use servlet standard way to handle 404 error. Add following code in web.xml
<error-page>
<exception-type>404</exception-type>
<location>/404error.html</location>
</error-page>

Related

Spring mvc - How to map all wrong request mapping to a single method

I am using a spring(version 3.2.5) annotation driven mvc application, basically I need be able to catch any wrong url to a single method then display an error message. For instance, if my correct url www.url.valid/login maps to a controller, if the user mistakenly insert loginw or any invalid url it should map to a default controller.
I have tried the following way:
package com.controller;
import static com.mns.utils.Constants.REGISTER_FAIL;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class DefaultSpringController {
#RequestMapping
public String forwardRequest(final HttpServletRequest request) {
return "redirect:/" + REGISTER_FAIL;
}
}
My Application context is as follows:
<!-- Default to DefaultSpringController -->
<bean id="defaultSpringController" class="com.controller.DefaultSpringController">
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="defaultHandler" ref="defaultSpringController"/>
</bean
But kept getting the following error:
nested exception is java.lang.ClassNotFoundException: com.controller.DefaultSpringController
I am not sure what I am missing here, and the controller is in the correct package.
Any idea how I can achieve it please?
Thanks in advance
It look like, you want a global page not found handler.
#ControllerAdvice
public class GlobalExceptionContoller {
#ExceptionHandler(NoHandlerFoundException.class)
public String handleError404(HttpServletRequest request, Exception e) {
return "pageNotFoundViewNameGoesHere";
}
}
You can set config at web.xml for invalid url. Note that invalid url usually throw the 404 error page.
<error-page>
<error-code>404</error-code>
<location>/error/error.jsp</location>
</error-page>
If it's not enough, you can go your controller method from this error page. But I usually use this config for error page instead of server side code. There have many other alternatives ways depend on your technologies usage but this way can reduce your code.
If you define a ExceptionHandler, keep in mind, that the wrong requests like /login/blabla will not throw an NoHandlerFoundException, because the suffix pattern math is activated by default.
Therefore just to define an ExceptionHandler is not enough to catch all wrong requests.
In this case follow these steps:
First step. configure your application to throw an exception for unmapped calls like /login/blabla (SuffixPattern + TrailingSlashMatch):
#Configuration
public class WebAppMainConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// will throw NoHandlerFoundException
configurer.setUseSuffixPatternMatch(false);
configurer.setUseTrailingSlashMatch(false);
}
[...]
}
Second step. define exception handler:
#ControllerAdvice
public class GlobalExceptionHandler {
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ExceptionHandler(NoHandlerFoundException.class)
public Object handleException(NoHandlerFoundException ex, HttpServletRequest request) {
[...]
ex.printStackTrace();
return "error";
}
}

Spring MVC Generic Exception Handling

I am using the following generic class to handle all types of exceptions in my app. It handles most of the exceptions but fails to for some such as "org.apache.tiles.impl.CannotRenderException". How can I make it catch all types of exceptions?
Some of the technologies that I am using are: Spring 4.0.0.RELEASE, Tiles 2.2, Maven 1.6, Spring Webflow 2.4.0.RELEAS
#ControllerAdvice
class GenericDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error/error";
private static final String DEFAULT_ERROR_SUBJECT = "Exception occurred";
final Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private MailService mailService;
#ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
throw e;
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
//send email to system admin
sendMessageToAdmin(e.toString(),req.getRequestURL().toString());
logger.error(e.toString());
return mav;
}
private void sendMessageToAdmin(String exceptionAsMessage, String url) {
try {
StringBuilder errorMessage = new StringBuilder();
errorMessage.append("Exception on request URL :");
errorMessage.append(url);
errorMessage.append("\n\n");
errorMessage.append("The Exception was: ");
errorMessage.append(exceptionAsMessage);
mailService.sendMailWithSubject(DEFAULT_ERROR_SUBJECT,errorMessage.toString());
} catch (Exception e) {
}
}
}
Thanks
The problem is that your handler catches the exception in your controller, but tiles exception are thrown in the view after the controller has finished its job. You can try to handle them using a filter :
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
...
try {
chain.doFilter(request, response);
}
catch (Exception ex) {
// your stuff
...
throw new ServletException(ex);
}
}
Independantly of that (or in addition), you can also ask the container to use some views when it find exceptions or when the controllers uses sendError with some configuration in the web.xml file :
<!-- error-code related error pages -->
<error-page>
<error-code>404</error-code>
<location>/ErrorHandler</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/ErrorHandler</location>
</error-page>
<!-- exception-type related error pages -->
<error-page>
<exception-type>
javax.servlet.ServletException
</exception-type >
<location>/ErrorHandler</location>
</error-page>
<error-page>
<exception-type>java.io.IOException</exception-type >
<location>/ErrorHandler</location>
</error-page>
Yes, the Filter worked for me. I created an exception class that extends GenericFilterBean of Spring filter and registered it inside my ApplicationConfig.
public class ApplicationConfig implements WebApplicationInitializer {
.....
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
.....
FilterRegistration.Dynamic exception = servletContext.addFilter("genericExceptionFilter", new GenericExceptionFilter());
exception.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
.....
}
}
Thank you.

Exception Mapper not getting invoked in JAX-RS jerey

I am trying to configure an ExceptionMapper that will catch all the 404 related exception in my application. This is the first time I am trying t play around with this ExceptionMapper, hence facing lots of issue, might be missing something silly :(
Below is what I did in the ExceptionMapper class:
public class ClassNotFoundMapper implements ExceptionMapper<NotFoundException> {
#Override
public Response toResponse(NotFoundException ex) {
return Response.status(404).entity(ex.getMessage()).type("text/plain").build();
}
}
In web.xml I added this entry:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
com.service.rest;
com.service.exception
</param-value>
</init-param>
In the service I did this:
#GET
#Path("{param}")
public Response getName(#PathParam("param") String msg, #HeaderParam("name") String headerName) {
//SOME CODE HERE AND THEN CONDITIONALLY RETURN THE EXCEPTION
return Response.status(Response.Status.NOT_FOUND).entity("NOT FOUND").build();
}
The exception present in the ExceptionMapper is not getting invoked.
Please let me know what am I missing.
You're missing the #Provider annotation on your ClassNotFoundMapper.

How to handle exceptions thrown while rendering a view in Spring MVC?

I have a Spring MVC application which uses FreeMarker as View technology (But maybe the view technology doesn't really matter for my question). I need to intercept all exceptions which may get thrown during a request.
I have implemented a HandlerExceptionResolver but this resolver is only executed when the exception occurs within a controller. But when a controller returns a ModelAndView and the exception occurs while rendering the view (Because a variable was not found or something like this) then the exception resolver is not called and instead I get a stack trace in the browser window.
I also tried using an exception handler method within the controller which returns the view and annotated it with #ExceptionHandler but this also doesn't work (Most likely again because the exception is not thrown in the controller but in the view).
So is there some Spring mechanism where I can register an exception handler which captures view errors?
A word upfront: if you just need a "static" error page without much logic and model preparation, it should suffice to put a <error-page>-Tag in your web.xml (see below for an example).
Otherwise, there might be better ways to do this, but this works for us:
We use a servlet <filter> in the web.xml that catches all Exceptions and calls our custom ErrorHandler, the same we use inside the Spring HandlerExceptionResolver.
<filter>
<filter-name>errorHandlerFilter</filter-name>
<filter-class>org.example.filter.ErrorHandlerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>errorHandlerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The implementation looks essentially like this:
public class ErrorHandlerFilter implements Filter {
ErrorHandler errorHandler;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
try {
filterChain.doFilter(request, response);
} catch (Exception ex) {
// call ErrorHandler and dispatch to error jsp
String errorMessage = errorHandler.handle(request, response, ex);
request.setAttribute("errorMessage", errorMessage);
request.getRequestDispatcher("/WEB-INF/jsp/error/dispatch-error.jsp").forward(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
errorHandler = (ErrorHandler) WebApplicationContextUtils
.getRequiredWebApplicationContext(filterConfig.getServletContext())
.getBean("defaultErrorHandler");
}
// ...
}
I believe this should work pretty much the same for FreeMarker templates. Of course if your error view throws an error, you're more or less out of options.
To also catch errors like 404 and prepare the model for it, we use a filter that is mapped to the ERROR dispatcher:
<filter>
<filter-name>errorDispatcherFilter</filter-name>
<filter-class>org.example.filter.ErrorDispatcherFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>errorDispatcherFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/error/dispatch-error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/WEB-INF/jsp/error/dispatch-error.jsp</location>
</error-page>
The doFilter-Implementation looks like this:
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
// handle code(s)
final int code = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (code == 404) {
final String uri = (String) request.getAttribute("javax.servlet.error.request_uri");
request.setAttribute("errorMessage", "The requested page '" + uri + "' could not be found.");
}
// notify chain
filterChain.doFilter(servletRequest, servletResponse);
}
You could extends the DispatcherServlet.
In your web.xml replace the generic DispatcherServlet for your own class.
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>com.controller.generic.DispatcherServletHandler</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Later create your own class DispatcherServletHandler and extends from DispatcherServlet:
public class DispatcherServletHandler extends DispatcherServlet {
private static final String ERROR = "error";
private static final String VIEW_ERROR_PAGE = "/WEB-INF/views/error/view-error.jsp";
#Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
try{
super.doService(request, response);
} catch(Exception ex) {
request.setAttribute(ERROR, ex);
request.getRequestDispatcher(VIEW_ERROR_PAGE).forward(request, response);
}
}
}
And in that page we only have to show a message to the user.
Not sure if my solution works with the problem you're having. Ill just post the way i catch my exceptions to ensure no stack trace is show inside the browser:
I made an AbstractController class with a method that will handle a specific conflict like this:
public class AbstractController {
#ResponseStatus(HttpStatus.CONFLICT)
#ExceptionHandler({OptimisticLockingFailureException.class})
#ResponseBody
public void handleConflict() {
//Do something extra if you want
}
}
This way whenever an exception occurs the user will see a default HTTPResponse status. (eg. 404 Not Found etc..)
I extend this class on all my controller classes to make sure errors are redirected to the AbstractController. This way I don't need to use ExceptionHandler on a specific controller but I can add the globally to all my controllers. (by extending the AbstractController class).
Edit:
After another go on your question, I noticed you're getting errors in your view. Not sure if this way will catch that error..
Hope this helps!!

Exception Handling in Spring MVC 3 to Show a Custom Error Page for Invalid URL

I am using #ExceptionHandler in Spring mvc 3 application for exception handling and I have written a controller like this :
#Controller
public class ExceptionsController {
private static final Logger logger = Logger.getLogger(ExceptionsController.class);
#ExceptionHandler(IOException.class)
public ModelAndView handleIOException(IOException ex) {
logger.info("handleIOException - Catching: " + ex.getClass().getSimpleName());
return errorModelAndView(ex);
}
private ModelAndView errorModelAndView(Exception ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("name", ex.getClass().getSimpleName());
return modelAndView;
}
#ExceptionHandler({NullPointerException.class, NoSuchRequestHandlingMethodException.class})
public ModelAndView handleExceptionArray(Exception ex) {
logger.info("handleExceptionArray - Catching: " + ex.getClass().getSimpleName());
return errorModelAndView(ex);
}
#ExceptionHandler(DataFormatException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "My Response Status Change....!!")
public ModelAndView handleDataFormatException(DataFormatException ex, HttpServletResponse response) {
logger.info("Handlng DataFormatException - Catching: " + ex.getClass().getSimpleName());
return errorModelAndView(ex);
}
For this, I have configured spring servlet xml file with bean declaration of AnnotationMethodHandlerExceptionResolver
What I want is when a user modifies the URL, PageNotFound Exception should be handled by #ExceptionHandler.
My scenario is, in browser, when a user changes the URL manually that is not correct, in that case a 404 page is rendered by the server, in place of that I want to show an error page as it comes in Facebook
You don't need to use Spring for that. The 404 is created by your app server (e.g Tomcat). Depending on how your DispatcherServlet is set up, you can just add this to your web.xml to return a custom page:
<error-page>
<error-code>404</error-code>
<location>/error.html</location>
</error-page>
Just make sure /error.html is either served by your app server (not by the DispatcherServlet) or as a pass-through from a file by Spring.

Categories

Resources