Spring custom 404 error page tutorial

Spring custom 404 error page tutorial shows how to create custom 404 error pages in a Spring web application.

Spring custom 404 error page tutorial

Spring custom 404 error page tutorial

last modified October 18, 2023

Spring custom 404 error page tutorial shows how to create custom 404 error pages in a Spring web application.

Spring is a popular Java application framework for creating enterprise applications.

404 code

HTTP 404 or 404 Not Found is a Hypertext Transfer Protocol (HTTP) standard response code, in computer network communications, to indicate that the client was able to communicate with a given server, but the server could not find the requested resource.

Spring custom 404 error page example

The following application uses creates a custom 404 error page. By default, Tomcat’s 404 error page is displayed when the resource cannot be located.

src ├───main │ ├───java │ │ └───com │ │ └───zetcode │ │ ├───config │ │ │ MyWebInitializer.java │ │ │ WebConfig.java │ │ └───controller │ │ ControllerAdvisor.java │ │ MyController.java │ └───resources │ │ logback.xml │ └───templates │ │ index.html │ └───error │ 404.html └───test └───java

This is the project structure.

pom.xml

<?xml version=“1.0” encoding=“UTF-8”?> <project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>

&lt;groupId&gt;com.zetcode&lt;/groupId&gt;
&lt;artifactId&gt;custom404page&lt;/artifactId&gt;
&lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
&lt;packaging&gt;war&lt;/packaging&gt;

&lt;properties&gt;
    &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
    &lt;maven.compiler.source&gt;17&lt;/maven.compiler.source&gt;
    &lt;maven.compiler.target&gt;17&lt;/maven.compiler.target&gt;
&lt;/properties&gt;

&lt;dependencies&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;
        &lt;artifactId&gt;logback-classic&lt;/artifactId&gt;
        &lt;version&gt;1.4.0&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
        &lt;artifactId&gt;javax.servlet-api&lt;/artifactId&gt;
        &lt;version&gt;4.0.1&lt;/version&gt;
        &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework&lt;/groupId&gt;
        &lt;artifactId&gt;spring-webmvc&lt;/artifactId&gt;
        &lt;version&gt;5.3.23&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.thymeleaf&lt;/groupId&gt;
        &lt;artifactId&gt;thymeleaf-spring5&lt;/artifactId&gt;
        &lt;version&gt;3.0.11.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.thymeleaf&lt;/groupId&gt;
        &lt;artifactId&gt;thymeleaf&lt;/artifactId&gt;
        &lt;version&gt;3.0.11.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;

&lt;/dependencies&gt;

&lt;build&gt;
    &lt;plugins&gt;

        &lt;plugin&gt;
            &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
            &lt;artifactId&gt;maven-war-plugin&lt;/artifactId&gt;
            &lt;version&gt;3.3.2&lt;/version&gt;
        &lt;/plugin&gt;

    &lt;/plugins&gt;
&lt;/build&gt;

</project>

In the pom.xml file we have the following dependencies: logback-classic, javax.servlet-api, spring-webmvc, and thymeleaf-spring5, and thymeleaf.

resources/logback.xml

<?xml version=“1.0” encoding=“UTF-8”?> <configuration> <logger name=“org.springframework” level=“ERROR”/> <logger name=“com.zetcode” level=“INFO”/>

&lt;appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender"&gt;
    &lt;encoder&gt;
        &lt;Pattern&gt;%d{HH:mm:ss.SSS} %blue(%-5level) %magenta(%logger{36}) - %msg %n
        &lt;/Pattern&gt;
    &lt;/encoder&gt;
&lt;/appender&gt;

&lt;root&gt;
    &lt;level value="INFO" /&gt;
    &lt;appender-ref ref="consoleAppender" /&gt;
&lt;/root&gt;

</configuration>

This is the logback.xml configuration file.

com/zetcode/config/MyWebInitializer.java

package com.zetcode.config;

import org.springframework.context.annotation.Configuration; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.FrameworkServlet; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

@Configuration public class MyWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override
protected Class&lt;?&gt;[] getRootConfigClasses() {
    return null;
}

@Override
protected Class&lt;?&gt;[] getServletConfigClasses() {
    
    return new Class[]{WebConfig.class};
}

@Override
protected String[] getServletMappings() {
    
    return new String[]{"/"};
}

@Override
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
    var dispatcher = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
    dispatcher.setThrowExceptionIfNoHandlerFound(true);
    return dispatcher;
}

}

MyWebInitializer initializes the Spring web application. It contains one configuration class: WebConfig.

@Override protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) { var dispatcher = (DispatcherServlet) super.createDispatcherServlet(servletAppContext); dispatcher.setThrowExceptionIfNoHandlerFound(true); return dispatcher; }

With the setThrowExceptionIfNoHandlerFound, we configure Spring to throw NoHandlerFoundException when a resource is not found. If we comment this line, the web server’s 404 error page is shown.

com/zetcode/config/WebConfig.java

package com.zetcode.config;

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.thymeleaf.spring5.SpringTemplateEngine; import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; import org.thymeleaf.spring5.view.ThymeleafViewResolver;

@Configuration @EnableWebMvc @ComponentScan(basePackages = {“com.zetcode”}) public class WebConfig implements WebMvcConfigurer {

@Autowired
private ApplicationContext applicationContext;

@Bean
public SpringResourceTemplateResolver templateResolver() {

    var templateResolver = new SpringResourceTemplateResolver();

    templateResolver.setApplicationContext(applicationContext);
    templateResolver.setPrefix("classpath:/templates/");
    templateResolver.setSuffix(".html");

    return templateResolver;
}

@Bean
public SpringTemplateEngine templateEngine() {

    var templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver());
    templateEngine.setEnableSpringELCompiler(true);

    return templateEngine;
}

@Bean
public ViewResolver viewResolver() {

    var resolver = new ThymeleafViewResolver();
    var registry = new ViewResolverRegistry(null, applicationContext);

    resolver.setTemplateEngine(templateEngine());
    registry.viewResolver(resolver);

    return resolver;
}

}

The WebConfig configures the Thymeleaf template engine. The Thymeleaf template files are located in the templates subdirectory on the classpath.

com/zetcode/controller/MyController.java

package com.zetcode.controller;

import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping;

import java.time.LocalDateTime;

@Controller public class MyController {

@GetMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE)
public String home(Model model) {

    model.addAttribute("now", LocalDateTime.now());

    return "index";
}

}

MyController contains one route for the home page.

com/zetcode/controller/ControllerAdvisor.java

package com.zetcode.controller;

import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.NoHandlerFoundException;

@ControllerAdvice public class ControllerAdvisor {

@ExceptionHandler(NoHandlerFoundException.class)
public ModelAndView handle(Exception ex) {

    var mv = new ModelAndView();
    mv.addObject("message", ex.getMessage());
    mv.setViewName("error/404");

    return mv;
}

}

ControllerAdvisor contains a handler for the NoHandlerFoundException. It shows the 404.html error page, located in resources/templates/error/404.html.

resources/templates/error/404.html

<!doctype html> <html xmlns:th=“http://www.thymeleaf.org”> <head> <meta charset=“UTF-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <title>Resource not found</title> </head> <body>

<h2>404 - resource not found</h2>

<p> <span th:text="${message}” th:remove=“tag”></span> </p>

</body> </html>

The 404.html is our custom 404 error page.

resources/templates/index.html

<!DOCTYPE html> <html xmlns:th=“http://www.thymeleaf.org”> <head> <meta charset=“UTF-8”> <title>Home page</title> </head> <body>

<p> This is home page. </p>

<p> Today is <span th:text="${now}"></span> </p> </body> </html>

This is the home page.

In this article we have created a custom 404 error page in a Spring application.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Spring tutorials.

ad ad