DEV Community

Cover image for Beginner’s Guide to Exception Handling in Spring Boot
Mohammed Quasim D A
Mohammed Quasim D A

Posted on

Beginner’s Guide to Exception Handling in Spring Boot

This is continuation of Building a Student Management API with Spring Boot: A Step-by-Step Guide

github:https://github.com/mohammedQuasimDA/smart-campus
If you:

    Have doubts about any part of this guide
    Find something confusing or unclear
    Notice typos/errors
Enter fullscreen mode Exit fullscreen mode

Please reach out in the comments/DMs—I’d be delighted to:

    Clarify concepts
    Fix mistakes promptly
    Improve this resource for everyone
Enter fullscreen mode Exit fullscreen mode

Your feedback helps make this article more accurate and helpful for future readers!

Create an dir exception

Why Exception Handling Matters

When building REST APIs, you want to return meaningful and consistent error messages when something goes wrong (e.g., bad input, not found, duplicates). That’s where Spring Boot’s exception handling with @ControllerAdvice and @ExceptionHandler comes in

1. Define Custom Exceptions

Create custom exceptions for specific error cases. These extend RuntimeException

// Duplicate roll number
public class DuplicateRollNumberException extends RuntimeException {
    public DuplicateRollNumberException(String message) {
        super(message);
    }
}

Enter fullscreen mode Exit fullscreen mode
// Invalid department code
public class InvalidDepartmentCodeException extends RuntimeException {
    public InvalidDepartmentCodeException(String code) {
        super("Invalid department code: " + code);
    }
}

Enter fullscreen mode Exit fullscreen mode
// Student not found
public class StudentNotFoundException extends RuntimeException {
    public StudentNotFoundException(String rollNumber) {
        super("Student not found with roll number: " + rollNumber);
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Create a Standard Error Response Object

We want every error to return a consistent JSON structure.

public class ErrorResponse {
    private LocalDateTime timestamp;
    private int status;
    private String error;
    private String message;
    private String path;
    private Map<String, String> details; // Optional: for field validation errors

    // Constructor for general errors
    public ErrorResponse(HttpStatus status, String message) {
        this.timestamp = LocalDateTime.now();
        this.status = status.value();
        this.error = status.getReasonPhrase();
        this.message = message;
    }

    // Constructor for validation errors
    public ErrorResponse(HttpStatus status, String message, Map<String, String> details) {
        this(status, message);
        this.details = details;
    }

    // Getters and setters...
}


Enter fullscreen mode Exit fullscreen mode

3. Build a Global Exception Handler with @ControllerAdvice

This class handles all exceptions in one place

@ControllerAdvice
public class GlobalExceptionHandler {

    private String extractPath(WebRequest request) {
        return request.getDescription(false).replace("uri=", "");
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidationErrors(
            MethodArgumentNotValidException ex, WebRequest request) {

        Map<String, String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .collect(Collectors.toMap(
                        FieldError::getField,
                        FieldError::getDefaultMessage,
                        (existing, replacement) -> existing
                ));

        ErrorResponse response = new ErrorResponse(
                HttpStatus.BAD_REQUEST,
                "Validation failed",
                errors
        );
        response.setPath(extractPath(request));
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(DuplicateRollNumberException.class)
    public ResponseEntity<ErrorResponse> handleDuplicateRoll(
            DuplicateRollNumberException ex, WebRequest request) {
        ErrorResponse response = new ErrorResponse(HttpStatus.CONFLICT, ex.getMessage());
        response.setPath(extractPath(request));
        return new ResponseEntity<>(response, HttpStatus.CONFLICT);
    }

    @ExceptionHandler(StudentNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleStudentNotFound(
            StudentNotFoundException ex, WebRequest request) {
        ErrorResponse response = new ErrorResponse(HttpStatus.NOT_FOUND, ex.getMessage());
        response.setPath(extractPath(request));
        return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(InvalidDepartmentCodeException.class)
    public ResponseEntity<ErrorResponse> handleInvalidDepartment(
            InvalidDepartmentCodeException ex, WebRequest request) {
        ErrorResponse response = new ErrorResponse(HttpStatus.BAD_REQUEST, ex.getMessage());
        response.setPath(extractPath(request));
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleOtherErrors(
            Exception ex, WebRequest request) {
        ErrorResponse response = new ErrorResponse(
                HttpStatus.INTERNAL_SERVER_ERROR,
                "An unexpected error occurred"
        );
        response.setPath(extractPath(request));
        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}


Enter fullscreen mode Exit fullscreen mode

4. Example Output (JSON)

When a user sends bad data, your API will respond like this

:
{
  "timestamp": "2025-05-23T12:45:00",
  "status": 400,
  "error": "Bad Request",
  "message": "Validation failed",
  "path": "/api/students",
  "details": {
    "name": "Name must not be blank",
    "email": "Invalid email format"
  }
}

{
  "timestamp": "2025-05-23T12:45:00",
  "status": 404,
  "error": "Not Found",
  "message": "Student not found with roll number: 23CS001",
  "path": "/api/students/23CS001"
}


Enter fullscreen mode Exit fullscreen mode

Top comments (0)