When Spring MVC attempts to map incoming HTTP request parameters to controller method arguments, data binding failures trigger org.springframework.validation.BindException. This typically occurs during parameter validation or type conversion operations.
Exception Triggers
BindException surfaces in several scenario:
- Constraint Violations: When bean validation annotations (
@NotEmpty,@Pattern,@Range, etc.) fail - Type Mismatches: Attempting to convert request parameters to incompatible target types (e.g., alphabetic strings to numeric fields)
- Numeric Overflows: Values exceeding the capacity of the target numeric type (e.g., integers larger than
Integer.MAX_VALUE) - Custom Validation Failures: Business logic validation implemented through custom validators or
@AssertTruemethods
Global Exception Handler Implementation
Centralize BindException handling using @RestControllerAdvice:
@RestControllerAdvice
@Slf4j
public class ValidationExceptionHandler {
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<ErrorResponse> handleBindingErrors(
BindException ex,
WebRequest request) {
List<String> validationErrors = ex.getFieldErrors().stream()
.map(this::formatFieldError)
.collect(Collectors.toList());
ErrorResponse error = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
"Validation Failed",
validationErrors,
request.getDescription(false)
);
log.warn("Data binding error: {}", error);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
private String formatFieldError(FieldError error) {
return String.format("%s: %s (rejected value: %s)",
error.getField(),
error.getDefaultMessage(),
error.getRejectedValue());
}
}
Practical Examples
Example 1: Constraint Violation
Consider a product registration endpoint with validation constraints:
@RestController
@RequestMapping("/api/products")
public class ProductController {
@PostMapping
public ResponseEntity<String> createProduct(
@Valid @RequestBody ProductRequest request) {
return ResponseEntity.ok("Product created");
}
}
@Data
public class ProductRequest {
@NotEmpty(message = "Product name cannot be blank")
private String productName;
@DecimalMin(value = "0.01", message = "Price must be positive")
private BigDecimal unitPrice;
}
Submitting an empty productName or negative price triggers BindException, captured by the global handler and returned as a structured error response.
Example 2: Type Conversion and Overflow
Query parameters with numeric constraints:
@GetMapping("/inventory")
public List<Product> searchInventory(
@Valid SearchCriteria criteria,
PaginationParams paging) {
return inventoryService.findProducts(criteria, paging);
}
@Data
public class PaginationParams {
@Min(1)
private int pageIndex = 1;
@Range(min = 1, max = 100)
private int itemsPerPage = 20;
}
Requests with non-numeric pageIndex values (e.g., "index") or overflow values (e.g., "9999999999999" for Integer fields) generate BindException with specific error details indicating the rejected value and required type.
Example 3: Custom Validation Logic
Complex validation using custom annotations:
@Data
public class OrderRequest {
@Valid
private CustomerInfo customerDetails;
@Future(message = "Delivery date must be in the future")
private LocalDate scheduledDelivery;
@AssertTrue(message = "Order must contain at least one item")
private boolean hasItems() {
return items != null && !items.isEmpty();
}
}
Validation failures in custom methods or nested objects propagate as BindException entries, accessible through ex.getFieldErrors() and ex.getGlobalErrors() respectively.