Mastering Custom Error Handling in Spring Boot 2.7: From BasicErrorController to ErrorPageRegistrar
This guide explains how to customize Spring Boot's default error handling—including enabling RFC‑7807 problem details, extending BasicErrorController, using @ControllerAdvice for JSON responses, creating custom HTML error pages, and registering error pages directly with the embedded servlet container.
1. Introduction
By default Spring Boot registers a global /error mapping that returns JSON for machine clients and an HTML "white‑label" view for browsers. To customize this behavior you can define a bean named error, set server.error properties, implement ErrorController, or provide an ErrorAttributes bean.
2. Extending the Default Controller
The BasicErrorController can be subclassed to handle additional media types. For example, extending it and adding a @RequestMapping method with a produces attribute lets you return custom content for application/problem+json (RFC 7807).
{
"type": "http://www.pack.com/users/666",
"title": "Unknown project",
"status": 404,
"detail": "xxxxx",
"instance": "/users/666"
}Enable RFC 7807 support in Spring Framework 6+ by setting spring.mvc.problemdetails.enabled=true.
3. Custom JSON Responses with @ControllerAdvice
@RestControllerAdvice(basePackageClasses = SomeController.class)
public class MyControllerAdvice extends ResponseEntityExceptionHandler {
@ExceptionHandler(Exception.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;
}
}4. Custom HTML Error Pages
Place static HTML files or template files under /error. The file name must match the exact status code or a pattern.
src/
+- main/
+- resources/
+- public/
+- error/
+- 404.htmlFor template‑based pages (e.g., FreeMarker) use:
src/
+- main/
+- resources/
+- templates/
+- error/
+- 5xx.ftlhFor more complex mapping, implement ErrorViewResolver:
@Component
public class PackErrorViewResolver implements ErrorViewResolver {
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
if (status == HttpStatus.INTERNAL_SERVER_ERROR) {
return new ModelAndView("error");
}
return null;
}
}5. Registering Error Pages Directly with the Container
@Configuration
public class PackErrorPagesConfiguration {
@Bean
public ErrorPageRegistrar errorPageRegistrar() {
return this::registerErrorPages;
}
private void registerErrorPages(ErrorPageRegistry registry) {
registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
}
}6. How Spring Boot Registers Default Error Pages (Tomcat Example)
Spring Boot’s embedded Tomcat uses TomcatServletWebServerFactory, which implements ErrorPageRegistry. During startup it iterates over all ErrorPage beans and adds them to Tomcat’s context.
public class TomcatServletWebServerFactory {
protected void configureContext(...) {
for (ErrorPage errorPage : getErrorPages()) {
org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage =
new org.apache.tomcat.util.descriptor.web.ErrorPage();
tomcatErrorPage.setLocation(errorPage.getPath());
tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
context.addErrorPage(tomcatErrorPage);
}
}
}Spring Boot also registers an ErrorPageRegistrarBeanPostProcessor that discovers all ErrorPageRegistrar beans and invokes them.
public class ErrorPageRegistrarBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ErrorPageRegistry) {
postProcessBeforeInitialization((ErrorPageRegistry) bean);
}
return bean;
}
private void postProcessBeforeInitialization(ErrorPageRegistry registry) {
for (ErrorPageRegistrar registrar : getRegistrars()) {
registrar.registerErrorPages(registry);
}
}
private Collection<ErrorPageRegistrar> getRegistrars() {
if (this.registrars == null) {
this.registrars = new ArrayList<>(
this.beanFactory.getBeansOfType(ErrorPageRegistrar.class, false, false).values());
this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);
this.registrars = Collections.unmodifiableList(this.registrars);
}
return this.registrars;
}
}Note: Custom ErrorPageRegistrar implementations can implement Ordered to control registration priority.
All the above steps provide a comprehensive way to replace or extend Spring Boot’s default error handling behavior.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
