✅ Returning meaningful error messages to the client.
✅ Improving API reliability and preventing crashes.
✅ Enhancing debugging and logging.
🔹 Reference to the Previous Tutorial
In our previous tutorial, we built a RESTful API for User Management using Spring Boot, MySQL, and Spring Data JPA. Now, we will extend that tutorial by implementing exception handling in the same application.
Prerequite tutorial: Building RESTful Web Services Using Spring Boot, Spring Data JPA, and MySQL
🚀 Step 1: Create a Custom Error Response Class
📌 We need a standard structure to return error messages when exceptions occur.
📌 Create ErrorResponse.java
inside net.javaguides.usermanagement.exception
package net.javaguides.usermanagement.exception;
import java.time.LocalDateTime;
public class ErrorResponse {
private String message;
private int status;
private LocalDateTime timestamp;
public ErrorResponse(String message, int status) {
this.message = message;
this.status = status;
this.timestamp = LocalDateTime.now();
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public LocalDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
}
📌 Explanation
✔️ message → Stores the error message.
✔️ status → Stores the HTTP status code (e.g., 404, 500).
✔️ timestamp → Stores the time the error occurred.
✔️ Constructor initializes timestamp
automatically.
🚀 Step 2: Create a Global Exception Handler
Instead of handling exceptions inside each controller, we will use @RestControllerAdvice
to centralize exception handling.
📌 Create GlobalExceptionHandler.java
inside net.javaguides.usermanagement.exception
package net.javaguides.usermanagement.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
// Handle ResourceNotFoundException
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
ErrorResponse errorResponse = new ErrorResponse(
ex.getMessage(),
HttpStatus.NOT_FOUND.value()
);
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
// Handle Generic Exceptions
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
ErrorResponse errorResponse = new ErrorResponse(
"An unexpected error occurred: " + ex.getMessage(),
HttpStatus.INTERNAL_SERVER_ERROR.value()
);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
📌 Explanation of GlobalExceptionHandler.java
✔️ @RestControllerAdvice
→ Makes this class a global exception handler for all controllers.
✔️ @ExceptionHandler(ResourceNotFoundException.class)
→ Handles cases where a user is not found in the database.
✔️ @ExceptionHandler(Exception.class)
→ Catches all other unhandled exceptions and returns a 500 (Internal Server Error).
🚀 Step 3: Create ResourceNotFoundException
📌 Create ResourceNotFoundException.java
inside net.javaguides.usermanagement.exception
package net.javaguides.usermanagement.exception;
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
🚀 Step 4: Modify the Service Layer to Use ResourceNotFoundException
📌 Modify UserServiceImpl.java
inside net.javaguides.usermanagement.service.impl
@Override
public UserDto getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id));
return userMapper.toDto(user);
}
@Override
public UserDto updateUser(Long id, UserDto userDto) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id));
user.setFirstName(userDto.firstName());
user.setLastName(userDto.lastName());
user.setEmail(userDto.email());
user.setDateOfBirth(userDto.dateOfBirth());
User updatedUser = userRepository.save(user);
return userMapper.toDto(updatedUser);
}
@Override
public void deleteUser(Long id) {
if (!userRepository.existsById(id)) {
throw new ResourceNotFoundException("User not found with id: " + id);
}
userRepository.deleteById(id);
}
📌 Explanation
✔️ Uses orElseThrow()
to throw ResourceNotFoundException
if a user does not exist.
✔️ Helps return 404 Not Found instead of null values.
🚀 Step 5: Testing Exception Handling Using Postman
✅ 1. Testing ResourceNotFoundException
❌ Get a Non-Existent User (GET Request)
📌 GET Request URL:
http://localhost:8080/api/users/100
✅ 2. Testing Generic Exception Handling
❌ Invalid URL (POST Request)
📌 POST Request URL:
Valid URL: http://localhost:8080/api/users
Invalid URL:
http://localhost:8080/api/users/1
🚀 Step 6: Logging Exceptions (Optional, Recommended)
To improve debugging, we can log errors when exceptions occur.
📌 Modify GlobalExceptionHandler.java
to include logging
package net.javaguides.usermanagement.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
logger.error("Resource Not Found: " + ex.getMessage());
ErrorResponse errorResponse = new ErrorResponse(ex.getMessage(), HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
logger.error("Internal Server Error: " + ex.getMessage(), ex);
ErrorResponse errorResponse = new ErrorResponse("An unexpected error occurred: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value());
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
📌 Explanation
✔️ Uses SLF4J Logger to log errors for better debugging.
✔️ Helps track errors in production environments.
🎯 Summary: What We Achieved
✔️ Implemented exception handling in Spring Boot.
✔️ Created a GlobalExceptionHandler
for centralized error handling.
✔️ Handled ResourceNotFoundException
for missing users.
✔️ Tested exception handling using Postman.
✔️ Added logging for better debugging.
🚀 Next Steps
🔹 Add Spring Security for authentication & authorization.
🔹 Implement custom validation for user input.
🔹 Deploy the application to AWS, Heroku, or Docker.
🎉 Congratulations! You now have a fully functional, production-ready Spring Boot API with proper exception handling! 🚀🔥
Comments
Post a Comment
Leave Comment