Trying to provide a custom 404 error page in a web application that, to the best of my knowledge, uses Java Config (thus no web.xml).
We have the following versions of the related libraries: spring ("5.1.2.RELEASE"), spring-security ("5.1.1.RELEASE").
Disclaimer
I have checked different approaches here in StackOverflow. Please don't suggest results for web.xml, Thymeleaf or Spring Boot. This is not applicable.
Among others; I tried with the following approaches:
None produced the expected result (that is, still getting the default webserver layout and error).
Controller annotation approach
exception package
package ...; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.NoHandlerFoundException; @ControllerAdvice public class GlobalExceptionHandler { // Option A (used as an alternative to option B) //@ExceptionHandler(Exception.class) //public String handle(Exception ex) { // return "redirect:/404"; //} @RequestMapping(value = {"/404"}, method = RequestMethod.GET) public String NotFoundPage() { return "404"; } // Option B (used as an alternative to option A) @ExceptionHandler(Exception.class) public ResponseEntity<String> handleNoHandlerFoundException(GlobalExceptionHandler ex) { ResponseEntity responseEntity = new ResponseEntity<>(new RestClientException("Testing exception"), HttpStatus.NOT_FOUND); return responseEntity; } }init class
package ...; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.http.ResponseEntity; import org.springframework.http.HttpStatus; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; @Configuration @ComponentScan("...") @EnableWebMvc @EnableTransactionManagement @PropertySource("classpath:application.properties") public class WebAppConfig extends WebMvcConfigurerAdapter { @ExceptionHandler({ Exception.class }) public ResponseEntity<RestClientException> handle(NoHandlerFoundException e) { return new ResponseEntity<>(new RestClientException("Testing exception"), HttpStatus.NOT_FOUND); } ... @Override public void addViewControllers(ViewControllerRegistry registry) { super.addViewControllers(registry); registry.addViewController("/404.jsp").setViewName("404"); } }
There is also an Initializer class (public class Initializer implements WebApplicationInitializer), which seems to conflict with some suggested options (defined here and here); so the webapp-init class is not modified.
web.xml approach
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="ROOT" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<error-page>
<error-code>404</error-code>
<location>/error</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error</location>
</error-page>
</web-app>
The 404.jsp or 404.html files are placed (currently for testing purposes at all the following locations):
src/main/resources
├── ...
├── error
│ └── 404.html
├── public
│ ├── 404.html
│ └── error
│ └── 404.html
├── templates
│ └── 404.html
└── ...
src/main/webapp/WEB-INF/
├── error.jsp
├── tags
│ └── ...
└── views
├── 404.html
├── 404.jsp
├── error.jsp
└── ...
Any idea on what is missing or wrong?
Although not as clear as I would like, this is a sort of working version to at least provide some customisation to error pages. It is a first approach but hopefully can help others.
The list of handled exceptions is not extensive, but mainly addressing 404 errors (
NoHandlerFoundException) and other typical errors likeInternalServerErrorExceptionandNullPointerException, to try to catch them all in the end with a generic error for everything else that is anException).Note that this is not covering other exceptions related to e.g. bad syntax in a JSTL template (
org.apache.jasper.*; exceptions that cannot apparently be caught here).These are the related changes and additions to the source base:
CustomSimpleMappingExceptionResolver.java (provide generic exceptions, yet logs details)
WebAppConfig.java (registers custom exception resolver as an exception handler)
Initializer.java (adding
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);; maybe not needed)Structure of views and tags related to error classes:
src/main/webapp/WEB-INF/tags/error.tag
src/main/webapp/WEB-INF/views/error/404.jsp
src/main/webapp/WEB-INF/views/error/500.jsp
src/main/webapp/WEB-INF/views/error/generic.jsp