When a client sends data that fails basic formatting rules (e.g., a blank name or a negative transaction amount), Spring Boot automatically intercepts the request before it reaches your controller and throws a standard
Important
MethodArgumentNotValidException
Because this exception= comes directly= from the Spring Framework, it doesn't extend our custom BusinessException. However, we can handle it seamlessly within the same GlobalExceptionHandler to keep the API responses unified.
Step 1: Add Validation Constraints to Your Request DTO
Use standard Jakarta Bean Validation annotations (like @NotBlank, @Positive) on your incoming data transfer objects (DTOs).
public class AccountCreationRequest {
@NotBlank(message = "Account holder name cannot be blank")
private String holderName;
@Positive(message = "Initial deposit amount must be greater than zero")
private double initialDeposit;
// Getters and Setters
}Step 2: Activate Validation in Your Controller
Place the @Valid annotation right before your request body in the controller method. This tells Spring to validate the incoming JSON before executing any code.
@RestController
@RequestMapping("/api/accounts")
public class AccountController {
@PostMapping
public ResponseEntity<AccountResponse> createAccount(@Valid @RequestBody AccountCreationRequest request) {
// If validation fails, this block is never reached.
return ResponseEntity.ok(accountService.create(request));
}
}Step 3: Map Validation Failures in the Global Handler
Add a dedicated @ExceptionHandler method inside your existing GlobalExceptionHandler class. Since a request might contain multiple invalid fields, we should extract all of them into a clean, scannable map.
@ControllerAdvice
public class GlobalExceptionHandler {
// 1. Handles all custom domain errors (AccountNotFoundException, etc.)
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessExceptions(BusinessException ex) {
ErrorResponse error = new ErrorResponse(
ex.getStatusCode(),
ex.getErrorCode(),
ex.getMessage(),
LocalDateTime.now()
);
return ResponseEntity.status(ex.getStatusCode()).body(error);
}
// 2. NEW: Handles all Spring payload validation errors (HTTP 400 Bad Request)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ValidationErrorResponse> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
// Loop through all field errors and extract the message
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
ValidationErrorResponse validationError = new ValidationErrorResponse(
HttpStatus.BAD_REQUEST.value(),
"ERR_VALIDATION_FAILED",
"Provided request payload contains invalid fields",
errors,
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(validationError);
}
}JSON Response Payload
If a frontend client attempts to create an account with a blank name and a negative deposit (-500.00), they will receive a clean 400 Bad Request status containing this precise, structured validation summary:
{
"status": 400,
"errorCode": "ERR_VALIDATION_FAILED",
"message": "Provided request payload contains invalid fields",
"fieldErrors": {
"holderName": "Account holder name cannot be blank",
"initialDeposit": "Initial deposit amount must be greater than zero"
},
"timestamp": "2026-09-23T00:35:00"
}- Service layers handle complex business flow rules using
Optionaland cleanBusinessExceptionconstructs. - Controllers stay entirely hands-off regarding validation logic.
- Clients always receive predictable, machine-readable JSON error contracts regardless of what went wrong inside the application.