Exception Handling in REST
In a REST API, things can go wrong – user not found, validation fails, server error. You should return meaningful error responses with proper HTTP status codes, not just exception stack traces.
Spring provides several ways to handle exceptions in REST APIs:
1. Using @ExceptionHandler in Controller
@RestController
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable int id) {
return userService.findById(id)
.orElseThrow(() -> new UserNotFoundException("User not found with id: " + id));
}
@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleUserNotFound(UserNotFoundException ex) {
return new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
}
}
2. Using @ControllerAdvice (Global Exception Handling)
This is the preferred approach – one class handles exceptions for all controllers.
@ControllerAdvice
public class GlobalExceptionHandler {
// Handle specific exceptions
@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleUserNotFound(UserNotFoundException ex) {
return new ErrorResponse(
"USER_NOT_FOUND",
ex.getMessage(),
LocalDateTime.now()
);
}
// Handle validation errors
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidationExceptions(MethodArgumentNotValidException ex) {
Map errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return new ErrorResponse("VALIDATION_FAILED", errors.toString());
}
// Handle all other exceptions (fallback)
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleGenericException(Exception ex) {
return new ErrorResponse(
"INTERNAL_ERROR",
"An unexpected error occurred"
);
}
}
3. Create a consistent Error Response class
public class ErrorResponse {
private String errorCode;
private String message;
private LocalDateTime timestamp;
// constructors, getters, setters
}
4. Using ResponseEntity for more control
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity handleUserNotFound(UserNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
"USER_NOT_FOUND",
ex.getMessage(),
LocalDateTime.now()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
Two Minute Drill
- Always return meaningful error responses, not stack traces.
- Use @ExceptionHandler in controllers for local handling.
- Use @ControllerAdvice for global exception handling across all controllers.
- Create a consistent ErrorResponse class for all errors.
- Use ResponseEntity for full control over response status and headers.
Need more clarification?
Drop us an email at career@quipoinfotech.com
