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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Custom Error Handling in Spring Boot 2.7: From BasicErrorController to ErrorPageRegistrar

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.html

For template‑based pages (e.g., FreeMarker) use:

src/
 +- main/
     +- resources/
         +- templates/
             +- error/
                 +- 5xx.ftlh

For 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backendspring-booterror-handlingCustom Error Pagespring-mvc
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.