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:


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