Handling exceptions effectively is critical to building robust and user-friendly Spring Boot applications. Whether you’re building REST APIs or web applications, users and clients need consistent, informative, and secure error responses.

This post dives into advanced exception handling in Spring Boot, covering:

  • Global error handling with @ControllerAdvice
  • Structured error responses for APIs
  • Custom error views for web apps
  • HTTP status mapping
  • Logging and diagnostics

Default Error Handling in Spring Boot

Out of the box, Spring Boot provides a global error controller (BasicErrorController) that returns:

  • JSON responses for REST clients
  • Whitelabel error pages for browser requests

Example default response:

{
"timestamp": "2024-11-16T09:23:18.326+00:00",
"status": 404,
"error": "Not Found",
"path": "/api/data"
}

While useful, it lacks structure, control, and branding. Let’s customize it for better UX and observability.


Creating a Global Exception Handler

Use @RestControllerAdvice to intercept exceptions thrown across your app:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex, HttpServletRequest request) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.NOT_FOUND.value(),
            ex.getMessage(),
            request.getRequestURI()
        );
        return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGeneric(Exception ex, HttpServletRequest request) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.INTERNAL_SERVER_ERROR.value(),
            "An unexpected error occurred",
            request.getRequestURI()
        );
        return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

Create a standard error response model:

public class ErrorResponse {
private int status;
private String message;
private String path;

    // constructor, getters, setters
}

This ensures a consistent structure across all API errors.


Handling Validation Errors

Spring automatically throws MethodArgumentNotValidException when validation fails. Handle it like this:

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleValidationErrors(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage()));
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}

This sends field-level error messages that frontend clients can use directly.


Custom Error Pages for Web Applications

For MVC or Thymeleaf-based apps, you can define custom HTML error pages:

  1. Place your templates in /src/main/resources/templates/error/

  2. Use the name pattern: error/404.html, error/500.html

Spring will resolve them based on the HTTP status code:

<!-- templates/error/404.html -->
<!DOCTYPE html>
<html>
<head><title>Page Not Found</title></head>
<body>
<h1>Oops! 404</h1>
<p>The page you are looking for does not exist.</p>
</body>
</html>

This allows you to match your site’s branding and improve the user experience.


Overriding Default