Template engines

In addition to REST web services, you can also use Spring MVC to handle dynamic HTML content. Spring MVC supports various templating technologies, including Thymeleaf, FreeMarker, and JSP. In addition, many other templating engines provide their own integrations with Spring MVC.

Spring Boot provides autoconfiguration support for the following templating engines:

If one of these template engines is used with the default configuration, then templates are automatically selected from src/main/ resources/templates.

Depending on how the application is running, the IDE may arrange the classpath differently. Running an application in the IDE from its main method results in a different ordering than running the application using Maven or Gradle, or from its packaged jar file. This may result in Spring Boot not being able to find the expected pattern. If this is a problem, you can change the classpath ordering in the IDE so that the module's classes and resources come first.

Error Handling

By default, Spring Boot displays /error that handles all errors appropriately, and it is logged as a "global" error page in the servlet container. For machine clients, it generates a JSON response with a detailed description of the error, an HTTP status code, and an exception message. For browser clients, there is a "whitelabel" error view that renders the same data in HTML (to customize this, add a View that allows error).

There are a number of server.error properties that you can set if you want to customize the default error logic.

To completely replace the default logic, you can implement a ErrorController and register a bean definition of that type, or add a bean of type ErrorAttributes to use the existing mechanism but replace the content.

BasicErrorController can be used as a base class for a custom ErrorController. This is especially practical if you need to add a handler for a new content type (the default handler only handles text/html and passes a fallback for everything else). To do this, extend BasicErrorController, add a public method with the @RequestMapping annotation that has the produces attribute, and create a bean of a new type.

In addition, you can define a class annotated with @ControllerAdvice to customize the returned JSON document for a specific controller and/or exception type, as shown in the following example:

Java

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
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.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@ControllerAdvice(basePackageClasses = SomeController.class)
public class MyControllerAdvice extends ResponseEntityExceptionHandler {
    @ResponseBody
    @ExceptionHandler(MyException.class)
    public ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(new MyErrorBody(status.value(), ex.getMessage()), status);
    }
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer code = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        HttpStatus status = HttpStatus.resolve(code);
        return (status != null) ? status : HttpStatus.INTERNAL_SERVER_ERROR;
    }
}
Kotlin

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.ResponseBody
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
import javax.servlet.RequestDispatcher
import javax.servlet.http.HttpServletRequest
@ControllerAdvice(basePackageClasses = [SomeController::class])
class MyControllerAdvice : ResponseEntityExceptionHandler() {
    @ResponseBody
    @ExceptionHandler(MyException::class)
    fun handleControllerException(request: HttpServletRequest, ex: Throwable): ResponseEntity<*> {
        val status = getStatus(request)
        return ResponseEntity(MyErrorBody(status.value(), ex.message), status)
    }
    private fun getStatus(request: HttpServletRequest): HttpStatus {
        val code = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE) as Int
        val status = HttpStatus.resolve(code)
        return status ?: HttpStatus.INTERNAL_SERVER_ERROR
    }
}

In the previous example, if MyException is thrown by a controller defined in the same package as SomeController, instead of the ErrorAttributes view, the JSON representation of the POJO object MyErrorBody is used .

In some cases, errors handled at the controller level are not captured by the metrics framework. Applications can ensure that such exceptions are recorded in request metrics by configuring the handled exception as a request attribute:

Java

import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
@Controller
public class MyController {
    @ExceptionHandler(CustomException.class)
    String handleCustomException(HttpServletRequest request, CustomException ex) {
        request.setAttribute(ErrorAttributes.ERROR_ATTRIBUTE, ex);
        return "errorView";
    }
}
Kotlin

import org.springframework.boot.web.servlet.error.ErrorAttributes
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.ExceptionHandler
import javax.servlet.http.HttpServletRequest
@Controller
class MyController {
    @ExceptionHandler(CustomException::class)
    fun handleCustomException(request: HttpServletRequest, ex: CustomException?): String {
        request.setAttribute(ErrorAttributes.ERROR_ATTRIBUTE, ex)
        return "errorView"
    }
}

Custom error pages

If you want to display a custom HTML error page for a given status code, you can add the file to the /error directory. Error pages can be either static HTML (that is, added to any of the static resource directories) or built using templates. The file name must be the exact status code or series mask.

For example, to display a 404 with a static HTML file, the directory structure should look like this:

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

To display all 5xx errors using the FreeMarker template, the directory structure should be as follows :

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftlh
             +- <other templates>

For more complex displays, you can also add beans that implement the ErrorViewResolver interface, as shown in following example:

Java

import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.ModelAndView;
public class MyErrorViewResolver implements ErrorViewResolver {
    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
         // Use the request or status to desire to return ModelAndView
        if (status == HttpStatus.INSUFFICIENT_STORAGE) {
            // Here we can add custom model values
            new ModelAndView("myview");
        }
        return null;
    }
}
z
Kotlin

import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver
import org.springframework.http.HttpStatus
import org.springframework.web.servlet.ModelAndView
import javax.servlet.http.HttpServletRequest
class MyErrorViewResolver : ErrorViewResolver {
    override fun resolveErrorView(request: HttpServletRequest, status: HttpStatus,
            model: Map<String, Any>): ModelAndView? {
        // Use the request or status to desire to return ModelAndView
        if (status == HttpStatus.INSUFFICIENT_STORAGE) {
            // Here we can add custom model values
            return ModelAndView("myview")
        }
        return null
    }
}

You can also use regular Spring MVC features, such as methods marked with the @ExceptionHandler annotation and the @ControllerAdvice. ErrorController then catches any unhandled exceptions.

Displaying error pages outside of Spring MVC

For applications not using Spring MVC, you can use the ErrorPageRegistrar interface for direct registration of ErrorPages. This abstraction works directly with the underlying built-in servlet container and works well even if you don't have DispatcherServlet for Spring MVC.

Java

import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@Configuration(proxyBeanMethods = false)
public class MyErrorPagesConfiguration {
    @Bean
    public ErrorPageRegistrar errorPageRegistrar() {
        return this::registerErrorPages;
    }
    private void registerErrorPages(ErrorPageRegistry registry) {
        registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
    }
}
Kotlin

import org.springframework.boot.web.server.ErrorPage
import org.springframework.boot.web.server.ErrorPageRegistrar
import org.springframework.boot.web.server.ErrorPageRegistry
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpStatus
@Configuration(proxyBeanMethods = false)
class MyErrorPagesConfiguration {
    @Bean
    fun errorPageRegistrar(): ErrorPageRegistrar {
        return ErrorPageRegistrar { registry: ErrorPageRegistry -> registerErrorPages(registry) }
    }
    private fun registerErrorPages(registry: ErrorPageRegistry) {
        registry.addErrorPages(ErrorPage(HttpStatus.BAD_REQUEST, "/400"))
    }
}
If you register an ErrorPage using a path that is ultimately processed using a Filter ( which is common in some non-Spring web frameworks such as Jersey and Wicket), the Filter must be explicitly registered as an ERROR manager, as shown in the following example:
Java

import java.util.EnumSet;
import javax.servlet.DispatcherType;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {
    @Bean
    public FilterRegistrationBean<MyFilter> myFilter() {
        FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(new MyFilter());
        // ...
        registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
        return registration;
    }
}
Kotlin

import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.EnumSet
import javax.servlet.DispatcherType
@Configuration(proxyBeanMethods = false)
class MyFilterConfiguration {
    @Bean
    fun myFilter(): FilterRegistrationBean<MyFilter> {
        val registration = FilterRegistrationBean(MyFilter())
        // ...
        registration.setDispatcherTypes(EnumSet.allOf(DispatcherType::class.java))
        return registration
    }
}

Note that the FilterRegistrationBean does not contain the ERROR manager type by default.

Error handling during WAR deployment

When deployed to a servlet container, Spring Boot uses its error page filter to redirect a request with an error code to the appropriate error page. This is necessary because the servlet specification does not provide an API for logging error pages. Depending on the container you deploy your war file to and the technologies your application uses, additional configuration may be required.

The error page filter will only be able to redirect the request to the correct error page if the answer has not yet been recorded. By default, WebSphere Application Server 8.0 and later captures the response after the servlet service method completes successfully. You should disable this logic by setting the com.ibm.ws.webcontainer.invokeFlushAfterService parameter to false.

If you are using Spring Security and want to get accessing the principal on the error page, you need to configure the Spring Security filter to be called when errors are sent. To do this, set the spring.security.filter.dispatcher-types property to async, error, forward, request.

CORS support

Cross-Origin Resource Sharing (CORS) is a W3C specification implemented by most browsers, which allows flexibility in determining what types of cross-domain requests will be authorized, and is intended to replace some less secure and less efficient approaches , such as IFRAME or JSONP.

Since version 4.2, Spring MVC supports CORS. Using a controller method for CORS configuration with @CrossOrigin annotations in a Spring Boot application does not require any special configuration. The global CORS configuration can only be defined by registering a WebMvcConfigurer bean with the addCorsMappings(CorsRegistry) method configured, as shown in the following example:

Java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration(proxyBeanMethods = false)
public class MyCorsConfiguration {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**");
            }
        };
    }
}
Kotlin

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.CorsRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration(proxyBeanMethods = false)
class MyCorsConfiguration {
    @Bean
    fun corsConfigurer(): WebMvcConfigurer {
        return object : WebMvcConfigurer {
            override fun addCorsMappings(registry: CorsRegistry) {
                registry.addMapping("/api/**")
            }
        }
    }
}