Battle_Slug
Battle_Slug

Reputation: 2105

Definining exception handler in Spring 4 to return custom 404 and 500 pages

My intention is to define one global custom 404 page for all my controllers and one global 500 page for all the exceptions. I read a bunch of questions here + all the articles from Google first results page.

I have a WebInitializer:

public class MyWebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) throws ServletException {
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
          rootContext.register(WebAppConfiguration.class);
          AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
        DispatcherServlet servlet = new DispatcherServlet(dispatcherContext);
        servlet.setThrowExceptionIfNoHandlerFound(true);
        ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", servlet);
          dispatcher.setLoadOnStartup(1);
          dispatcher.addMapping("/");
    }
}

And now I have following exceptions handler:

    @ControllerAdvice
    public class CustomGlogalExceptionHandler {
    public static final String DEFAULT_ERROR_VIEW = "/error/500";

    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    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("url", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);
        return mav;
    }

    @ExceptionHandler(value = NoHandlerFoundException.class)
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    public ModelAndView noPageFoundHandler(HttpServletRequest req, Exception e) throws Exception {
        ModelAndView mav = new ModelAndView();
        mav.addObject("url", req.getRequestURL());
        mav.setViewName("/error/404");
        return mav;
    }
}

It works fine on hitting http://localhost:8080/test: I get my 404 custom page + response code 404. It also works fine if I throw artificially somewhere: it shows my 500 custom page + response code 500.

But if some real exception happens, I don't get neither 500 response code nor my custom 500 page. For example now I have org.hibernate.LazyInitializationException but what I see in Chrome console is just 200 response code though I see full stack trace in server console, and the exception is the last it shows. Also, if Thymeleaf template engine throws, it shows full stack trace in the browser. What else should I configure to make it work correctly? Thanks.

Upvotes: 0

Views: 1027

Answers (1)

kuhajeyan
kuhajeyan

Reputation: 11017

spring will not pickup your throwable class. [ExceptionHandlerExceptionResolver][1] expects exception or its subtypes.
so should be

@ExceptionHandler(value = Excepton.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)

Hence whatever your annotated get rethrown and handled to 500, rest goes without spring notice

 if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
            throw e;

Or.

Other option is to use SimpleMappingExceptionResolver, by just declaring it

<bean id="simpleMappingExceptionResolver" class=
     "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
      <map>
         <entry key="Exception" value="gen_error"/>

      </map>
    </property>

    <!-- See note below on how this interacts with Spring Boot -->
    <property name="defaultErrorView" value="error"/>
    <property name="exceptionAttribute" value="ex"/>

    <!-- Name of logger to use to log exceptions. Unset by default, 
           so logging is disabled unless you set a value. -->
    <property name="warnLogCategory" value="example.MvcLogger"/>
  </bean>

= bean

@Bean
public SimpleMappingExceptionResolver exceptionResolver() {
    SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();

    Properties exceptionMappings = new Properties();

    exceptionMappings.put("java.lang.Exception", "error/error");
    exceptionMappings.put("java.lang.RuntimeException", "error/error");

    exceptionResolver.setExceptionMappings(exceptionMappings);



    return exceptionResolver;
}

Upvotes: 1

Related Questions