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
Please reach out in the comments/DMs—I’d be delighted to:
Clarify concepts
Fix mistakes promptly
Improve this resource for everyone
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);
}
}
// Invalid department code
public class InvalidDepartmentCodeException extends RuntimeException {
public InvalidDepartmentCodeException(String code) {
super("Invalid department code: " + code);
}
}
// Student not found
public class StudentNotFoundException extends RuntimeException {
public StudentNotFoundException(String rollNumber) {
super("Student not found with roll number: " + rollNumber);
}
}
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...
}
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);
}
}
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"
}
Top comments (0)