Spring Boot Input Data Validation
1. Introduction to Input Data Validation in Spring Boot
Input data validation is crucial in ensuring that incoming data to a Spring Boot application is valid, consistent, and safe. Spring Boot provides strong support for handling validation using annotations from the javax.validation and hibernate-validator packages.
Spring Boot allows you to validate data at both the request level (incoming JSON or form data) and at the field level within Java objects (DTOs or Entities).
2. Enabling Validation
To enable validation, you must include the spring-boot-starter-validation dependency in your pom.xml (for Maven users) or build.gradle (for Gradle users).
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
3. Using Validation Annotations
Spring Boot uses standard validation annotations to validate incoming data. These annotations are placed on fields in a class that represents the request body or form data.
Common validation annotations:
- @NotNull: Ensures the field is not null.
- @NotEmpty: Ensures the field is not empty.
- @Size(min, max): Ensures the field has a size within specified bounds.
- @Min, @Max: Restricts numerical fields to a minimum or maximum value.
- @Email: Ensures the field contains a valid email format.
- @Pattern: Ensures the field matches a regular expression pattern.
4. Sample DTO Class with Validation Annotations
Let's create a simple DTO (Data Transfer Object) class that will hold input data with validation rules:
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class UserDTO {
@NotNull(message = "Name cannot be null")
@Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
private String name;
@NotEmpty(message = "Email cannot be empty")
@Email(message = "Email should be valid")
private String email;
@NotNull(message = "Age is required")
@Min(value = 18, message = "Age should be at least 18")
@Max(value = 100, message = "Age should be less than or equal to 100")
private Integer age;
// Getters and setters
}
5. Using @Valid or @Validated in Controller
To trigger validation in the controller, the @Valid or @Validated annotation is used on the request body parameter. If validation fails, Spring Boot automatically returns a 400 Bad Request response with validation errors.
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
@RestController
public class UserController {
@PostMapping("/users")
public ResponseEntity<String> createUser(@Valid @RequestBody UserDTO userDTO) {
// Business logic to validate and create the user
return ResponseEntity.ok("User is valid and created successfully!");
}
}
6. Default Error Response for Validation
If validation fails, Spring Boot returns a default error response in JSON format containing details of the validation errors. Here's an example:
{
"timestamp": "2024-09-30T15:30:00.123+00:00",
"status": 400,
"errors": [
"Email should be valid",
"Name must be between 2 and 50 characters",
"Age should be at least 18"
]
}
7. Customizing Validation Error Responses
We can create custom validation error responses by handling the MethodArgumentNotValidException in a global exception handler. This gives more control over the structure of the error response.
Step 1: Define a custom error response class.
import java.util.List;
@Getter
@Setter
public class ValidationErrorResponse {
private String message;
private List errors;
public ValidationErrorResponse(String message, List<String> errors) {
this.message = message;
this.errors = errors;
}
}
Step 2: Define a global exception handler to customize validation error responses.
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.http.HttpStatus;
import java.util.List;
import java.util.stream.Collectors;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ValidationErrorResponse> handleValidationExceptions(MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult()
.getAllErrors()
.stream()
.map(error -> ((FieldError) error).getField() + ": " + error.getDefaultMessage())
.collect(Collectors.toList());
ValidationErrorResponse response = new ValidationErrorResponse("Validation failed", errors);
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
}
8. Custom Error Response Example
When validation fails, the custom error response might look like this:
{
"message": "Validation failed",
"errors": [
"email: Email should be valid",
"name: Name must be between 2 and 50 characters",
"age: Age should be at least 18"
]
}
9. Validating Path Variables and Request Parameters
Spring Boot also supports validation of path variables and request parameters using the same validation annotations. The @Validated annotation is used at the controller method level for such cases.
import javax.validation.constraints.Min;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.validation.annotation.Validated;
@RestController
@Validated
public class UserController {
@GetMapping("/users/{id}")
public ResponseEntity<String> getUserById(@PathVariable("id") @Min(1) Long id) {
// If validation passes, return the user
return ResponseEntity.ok("User with ID: " + id);
}
}
10. Summary
- Validation in Spring Boot is achieved through annotations such as @NotNull, @Size, @Email, etc.
- Use @Valid in controllers to trigger validation on request bodies or forms.
- Handle validation errors globally using @ControllerAdvice and MethodArgumentNotValidException.
- Custom validation error responses can be created for better client interaction.
- Path variables and request parameters can also be validated using @Validated.