A guide to input validation with Spring Boot
Tags: codesecurity, java
Published: 2023-09-13 02:00:39


```
{"id":1,"name":"John Doe Updated","email":"john.doe.updated@example.com","password":"newP@ssW0rd!"}
```

If you're a developer working with Java, you likely know [Spring Boot](https://spring.io/projects/spring-boot), the robust framework that streamlines the development of stand-alone, production-grade, Spring-based applications. One of the many features it offers is [Bean Validation](https://beanvalidation.org/2.0-jsr380/spec/), which is a crucial aspect of any application to ensure data integrity and improve user experience.


Bean Validation can be used in Spring Boot applications for input and form validation, data validation prior to saving to a database, and enforcement of various security policies. With Bean Validation, developers can prevent errors, improve the overall quality of the application, and ensure that data is consistent throughout the application.


In this article, you'll learn more about the various applications of Bean Validation, including how to implement it in Spring Boot, enabling you to effectively utilize it for your own projects.


What is Bean Validation
-----------------------


Bean Validation is a feature that allows you to apply constraints to your application's data models to ensure that the data adheres to specific rules before it's processed or stored. This is achieved through the integration of the [Jakarta Bean Validation](https://beanvalidation.org/3.0/), which provides a set of annotations and interfaces to define and validate constraints.


By applying Bean Validation, developers can save time and resources by catching errors and inconsistencies early on in the development process. In addition, Bean Validation helps improve the overall user experience by reducing the amount of incorrect data that's entered.


Let's take a look at some common use cases of Bean Validation in application development:


### Input and form validation


One of the most common use cases of Bean Validation is input and form validation. This helps ensure that users input data in the correct format (i.e. sign-up form validation with email addresses, usernames, and passwords).


### Data validation prior to saving to a database


Another application of Bean Validation is data validation before saving it to a database. This technique helps ensure that the data entered is consistent and accurate. In turn, this helps prevent errors and reduces the risk of data corruption.


### Enforcement of security policies


Bean Validation can also be used for enforcing security policies by validating user input, or as data mapped from the database, to ensure that it meets specific criteria, such as a password strength requirement. Validation plays a key role in preventing [injection attacks](https://learn.snyk.io/lesson/sql-injection/) and helps prevent security vulnerabilities such as stored [cross-site scripting (XSS)](https://learn.snyk.io/lesson/xss/).


### Business logic validation


Bean Validation can be applied to validate business logic and ensure it meets specific requirements or standards. By defining validation constraints, such as password complexity or valid order item quantities and prices, Bean Validation can automatically check data and enforce the required rules. This helps to eliminate manual checking and ensures the application adheres to specific standards or requirements.


Implementing Spring Boot Bean Validation
----------------------------------------


In this tutorial, you will implement Bean Validation for a simple create, read, update, delete (CRUD) application with an in-memory database. Users provide their name, email, and password, which must meet specific criteria to be accepted. The high-level architecture used here consists of a backend server with RESTful APIs and an in-memory H2 database.


When a client application calls the web service (which is the application of this tutorial), the request goes to the `UserController.` After that, the controller applies data validation to the request. If the data is valid, the request proceeds to the `UserRepository` to communicate with the in-memory database and return the relevant response. Otherwise, the data is not valid and the request is sent back to the client with an error message:


![](https://res.cloudinary.com/snyk/image/upload/v1694445966/blog-bean-validation-flow.png)
### Prerequisites


Before diving into the implementation, ensure you have the following tools and technologies installed:


* [Java Development Kit (JDK)](https://docs.oracle.com/en/java/javase/18/install/overview-jdk-installation.html#GUID-8677A77F-231A-40F7-98B9-1FD0B48C346A) version 17 or above.
* An IDE, such as [IntelliJ IDEA](https://www.jetbrains.com/idea) or [Eclipse](https://www.eclipse.org/ide/). This tutorial uses [IntelliJ IDEA Community Edition](https://www.jetbrains.com/idea/download/).
* [Maven](https://maven.apache.org/download.cgi) version 3.8.6 or above.


### Create a new Spring Boot project


To create a new Spring Boot project, visit the [Spring Initializr](https://start.spring.io) and choose the following options:


* **Project type:** Maven Project
* **Language:** Java
* **Packaging:** Jar
* **Java version:** 17
* **Spring Boot:** 3.1.0 (SNAPSHOT)


Enter the following details in the **Project Metadata** section:


* **Group:** com.example
* **Artifact:** simple-crud-bean-validation
* **Name:** simple-crud-bean-validation
* **Description:** A simple CRUD application with Spring Boot
* **Package name:** com.example.simplecrud


And use the following dependencies:


* **Web:** Spring Web
* **Validation:** Bean Validation
* **H2 Database:** H2 Database
* **Spring Data JPA:** Spring Data and Hibernate


![](https://res.cloudinary.com/snyk/image/upload/v1694449260/blog-bean-validation-initializr.jpg)
Click **Generate** to download the project as a ZIP file. Then, extract the ZIP file and import the project into your preferred IDE.


### Implement the application


We'll be following the standard project structure for Spring Boot applications. Here's the complete project structure:


![](https://res.cloudinary.com/snyk/image/upload/v1694451958/blog-bean-validation-structure.jpg)
### User model


To implement the application, you need to start by creating the model directory inside `src/main/java/com/example/simplecrudbeanvalidation`. Then, inside the `model` directory, create a file named `User.java` and add the following fields and Bean Validation annotations to the **User class**:



```
import jakarta.persistence.*;
import jakarta.validation.constraints.*;

@Entity
@Table(name="users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotEmpty
    @Pattern(regexp = "[a-zA-Z0-9 ]")
    private String name;

    @NotNull
    @NotEmpty
    @Email
    @Pattern(regexp=".+@.+\\..+")
    private String email;

    @NotNull
    @NotEmpty
    @Size(min = 8, max = 64)
    private String password;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public User(Long id, String name, String email, String password){
        this.id = id;
        this.name = name;
        this.email = email;
        this.password = password;
    }

    //default constructor
    public User(){}
}
```

This code defines a Java class named `User` that represents a table in a database called `users` using [Jakarta Persistence API (JPA)](https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/entity) annotations. It has four instance variables — `id`, `name`, `email`, and `password` — with corresponding getters and setters. Each of these fields has certain validation constraints, such as `@NotNull`, `@NotEmpty`, `@Email`, `@Size`, and `@Pattern`. `@Pattern` is used to indicate that the password must have eight to sixty-four characters and contain at least one digit, one lowercase letter, one uppercase letter, and one special character. Additionally, the `@Pattern` is also used for the `name` property to prevent any script injections. The class can be used as a model class to store and retrieve user data from a database.


#### User repository


To handle CRUD operations using an in-memory database, create the `repository` directory inside `src/main/java/com/example/simplecrudbeanvalidation`. Then, inside the repository `directory`, create a file named `UserRepository.java` to define the `UserRepository` interface, as follows:



```
import com.example.simplecrudbeanvalidation.model.User;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends CrudRepository {
}
```

This code defines a Java interface called `UserRepository` that extends the `CrudRepository` interface provided by the [Spring Data JPA](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/). The `UserRepository` interface specifies two generic parameters for the `CrudRepository`: `User`, which is the entity type this repository manages, and `Long`, which is the entity's primary key type.


By extending `CrudRepository`, `UserRepository` inherits several methods for performing CRUD operations on the `User` entity. These methods can be used to interact with an in-memory database to manage `User` objects without the need for complex configuration or a full-fledged database server.


#### User controller


To create the `UserController` class that defines the RESTful API endpoints and validation logic, start by creating a directory named `controller` inside the `src/main/java/com/example/simplecrudbeanvalidation` directory. Inside the `controller` directory, create a file named `UserController.java` with the following content:



```
import com.example.simplecrudbeanvalidation.model.User;
import com.example.simplecrudbeanvalidation.repository.UserRepository;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/users")
@Validated
public class UserController
{
   @Autowired
   private UserRepository userRepository;

   @GetMapping
   public List getAllUsers() {
 return (List) userRepository.findAll();
 }

 @GetMapping("/{id}")
 public ResponseEntity getUserById(@PathVariable Long id) {
 Optional userOptional = userRepository.findById(id);
 if (userOptional.isPresent()) {
 return ResponseEntity.ok(userOptional.get());
 } else {
 return ResponseEntity.notFound().build();
 }
 }

 @PostMapping
 public User createUser(@Valid @RequestBody User user) {
 return userRepository.save(user);
 }

 @PutMapping("/{id}")
 public ResponseEntity updateUser(@PathVariable Long id, @Valid @RequestBody User updatedUser) {
 Optional userOptional = userRepository.findById(id);
 if (userOptional.isPresent()) {
 User user = userOptional.get();
 user.setName(updatedUser.getName());
 user.setEmail(updatedUser.getEmail());
 user.setPassword(updatedUser.getPassword());
 userRepository.save(user);
 return ResponseEntity.ok(user);
 } else {
 return ResponseEntity.notFound().build();
 }
 }

 @DeleteMapping("/{id}")
 public ResponseEntity deleteUser(@PathVariable Long id) {
 if (userRepository.existsById(id)) {
 userRepository.deleteById(id);
 return ResponseEntity.ok().build();
 } else {
 return ResponseEntity.notFound().build();
 }
 }
}
```

This code defines a Spring Boot `RestController` class called `UserController` that handles HTTP requests for managing `User` objects. The class contains five methods that handle different HTTP requests: retrieving all users, retrieving a single user by ID, creating a new user, updating an existing user, and deleting a user.


The `@Autowired` annotation is used to inject the `UserRepository` dependency into the `UserController` class. The `UserController` class is associated with the `/api/users` endpoint through the `@RequestMapping` annotation, which maps incoming HTTP requests to this controller. In addition, the `@Validated` annotation is used to enable the validation of request parameters. Overall, this code provides a RESTful API for CRUD operations on `User` objects using Spring Data and an in-memory database.


#### Configure the In-Memory Database


To configure the in-memory database, go to the `application.properties` under the `src/main/resources/` and add the following configurations:



```
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
```

This Spring Boot configuration sets up an H2 in-memory database named `testdb` and configures its driver, username, and password. It also specifies the H2 dialect that should be used for Hibernate-based database operations.


  

### Unit Test for Bean Validation


Once you've configured the in-memory database, you can begin unit testing for Bean Validation. To do so, create the `controller` directory inside `src/test/java/com/example/simplecrudbeanvalidation`. Then, inside the `controller` directory, create a file named `UserControllerTest.java` and write tests to verify that the Bean Validation constraints are working as expected.


In the `UserControllerTest` class, add the following code:



```
import com.example.simplecrudbeanvalidation.model.User;
import com.example.simplecrudbeanvalidation.repository.UserRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import java.util.Optional;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserRepository userRepository;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    public void createUser_validData_success() throws Exception {
        User newUser = new User(null, "John Doe", "john.doe@example.com", "p@ssW0rd!");
        User savedUser = new User(1L, newUser.getName(), newUser.getEmail(), newUser.getPassword());

        when(userRepository.save(any(User.class))).thenReturn(savedUser);

        mockMvc.perform(post("/api/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(newUser)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(savedUser.getId()))
                .andExpect(jsonPath("$.name").value(savedUser.getName()))
                .andExpect(jsonPath("$.email").value(savedUser.getEmail()))
                .andExpect(jsonPath("$.password").value(savedUser.getPassword()));
    }

    @Test
    public void createUser_invalidData_failure() throws Exception {
        User invalidUser = new User(null, "", "invalid-email", "123");

        mockMvc.perform(post("/api/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(invalidUser)))
                .andExpect(status().isBadRequest());
    }

    @Test
    public void getAllUsers() throws Exception {
        User user1 = new User(1L, "John Doe", "john.doe@example.com", "p@ssW0rd!");
        User user2 = new User(2L, "Jane Doe", "jane.doe@example.com", "p@ssW0rd!");
        when(userRepository.findAll()).thenReturn(Arrays.asList(user1, user2));
        mockMvc.perform(get("/api/users"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", hasSize(2)))
                .andExpect(jsonPath("$[0].id").value(user1.getId()))
                .andExpect(jsonPath("$[0].name").value(user1.getName()))
                .andExpect(jsonPath("$[0].email").value(user1.getEmail()))
                .andExpect(jsonPath("$[0].password").value(user1.getPassword()))
                .andExpect(jsonPath("$[1].id").value(user2.getId()))
                .andExpect(jsonPath("$[1].name").value(user2.getName()))
                .andExpect(jsonPath("$[1].email").value(user2.getEmail()))
                .andExpect(jsonPath("$[1].password").value(user2.getPassword()));
    }

    @Test
    public void getUserById_found() throws Exception {
        User user = new User(1L, "John Doe", "john.doe@example.com", "p@ssW0rd!");
        when(userRepository.findById(user.getId())).thenReturn(Optional.of(user));
        mockMvc.perform(get("/api/users/{id}", user.getId()))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(user.getId()))
                .andExpect(jsonPath("$.name").value(user.getName()))
                .andExpect(jsonPath("$.email").value(user.getEmail()))
                .andExpect(jsonPath("$.password").value(user.getPassword()));
    }

    @Test
    public void getUserById_notFound() throws Exception {
        Long userId = 1L;
        when(userRepository.findById(userId)).thenReturn(Optional.empty());
        mockMvc.perform(get("/api/users/{id}", userId))
                .andExpect(status().isNotFound());
    }
}
```

This code is a set of JUnit tests for the `UserController` class. The tests use the `MockMvc` class to simulate HTTP requests to the `/api/users` endpoint and verify that the corresponding methods in the `UserController` class handle them correctly. The tests cover scenarios such as creating a new user with valid and invalid data, retrieving all users, and retrieving a user by ID when the user exists and when it does not.


The `@SpringBootTest` annotation is used to load the application context, and the `@MockBean` annotation is used to mock the `UserRepository` dependency of the `UserController` class. Overall, these tests ensure that the `UserController` class functions correctly and responds appropriately to various HTTP requests.


To run the tests, open a terminal or shell, navigate to the root directory of your project, and run the following command:



```
mvn test
```

You should see that all your tests have passed:



```
…output omitted…
[INFO] Results:
[INFO] 
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
…output omitted…
```

### Run and test your APIs


In IntelliJ, right-click your `SimpleCrudBeanValidationApplication.java` file and select **Run 'SimpleCrudBeanValidationApplication.main()'**:


![](https://res.cloudinary.com/snyk/image/upload/v1694464568/blog-bean-validation-run.jpg)
Alternatively, you can run your application in the terminal or shell by using the following commands:



```
mvn clean install
java -jar target/simple-crud-bean-validation-0.0.1-SNAPSHOT.jar
```

After starting the Spring Boot application, the CRUD operations can be tested using curl commands from the terminal or shell, which is similar to how a frontend application or [Postman](https://www.postman.com/) would call these APIs.


To test the CRUD operations, begin by creating a user:



```
curl -X POST -H "Content-Type: application/json" -d '{"name":"John Doe","email":"john.doe@example.com","password":"p@ssW0rd!"}' http://localhost:8080/api/users
```

Your output looks like this:



```
{"id":1,"name":"John Doe","email":"john.doe@example.com","password":"p@ssW0rd!"} 
```

Then get all your users:



```
curl -X GET http://localhost:8080/api/users
```

Your output looks like this:



```
{"id":1,"name":"John Doe","email":"john.doe@example.com","password":"p@ssW0rd!"}
```

Next, get your user by ID. Replace  with the ID of the user you want to retrieve:



```
curl -X GET http://localhost:8080/api/users/
```

The following is the output:



```
{"id":1,"name":"John Doe","email":"john.doe@example.com","password":"p@ssW0rd!"}
```

Update the user by replacing  with the ID of the user you want to update:



```
curl -X PUT -H "Content-Type: application/json" -d '{"name":"John Doe Updated","email":"john.doe.updated@example.com","password":"newP@ssW0rd!"}' http://localhost:8080/api/users/
```

Your output looks like this:



```
{"id":1,"name":"John Doe Updated","email":"john.doe.updated@example.com","password":"newP@ssW0rd!"}
```

Then, delete a user by replacing  with the ID of the user you want to delete:



```
curl -X DELETE http://localhost:8080/api/users/
```

The output of this command is empty.


Using these curl commands, you can test the CRUD operations of your User RESTful API. Remember to replace  with the appropriate user ID in each command. By following these steps, you can interact with your Spring Boot application and verify that the Bean Validation constraints are functioning as expected during runtime.


If you attempt to run these curl requests again with incorrect entries, such as an invalid email, password, or empty values, you will find that the API returns a bad request error. This error message indicates that the validation performed on the controller level, also known as Bean Validation, did not pass.


Let's look at some examples of bad requests. Here, you create a user with an invalid email address:



```
curl -X POST -H "Content-Type: application/json" -d '{"name":"John Doe","email":"johexample","password":"p@ssW0rd!"}' http://localhost:8080/api/users
```

Your output looks like this:



```
{"timestamp":"2023-03-28T10:27:21.884+00:00","status":400,"error":"Bad Request","path":"/api/users"}
```

And here, you create a user with an invalid password:



```
curl -X POST -H "Content-Type: application/json" -d '{"name":"John Doe","email":"joh@example.com","password":"test"}' http://localhost:8080/api/users
```

The following is the output:



```
{"timestamp":"2023-03-28T10:29:30.793+00:00","status":400,"error":"Bad Request","path":"/api/users"}
```

Concluding our validation
-------------------------


In this article, you learned how to implement Spring Boot Bean Validation in a simple CRUD application. You created a project using Spring Initializr, added necessary dependencies, and implemented a user model with Bean Validation annotations. You also implemented a `UserRepository` and a `UserController` with RESTful API endpoints that utilize Bean Validation and wrote tests to verify that the Bean Validation constraints are working as expected.


To further improve the security of your application, use [Snyk Code](/product/snyk-code), a SAST that detects insecure code patterns that can lead to injection attacks. You can use the [Snyk Security plugin for IntelliJ IDE](https://plugins.jetbrains.com/plugin/10972-snyk-security--code-open-source-container-iac-configurations) extension to integrate this analysis into your development workflow.


Now that you understand how Spring Boot Bean Validation works, you can implement it in your own projects, ensuring data integrity and improving user experience. To explore the completed code, please visit the following [GitHub repository](https://github.com/See4Devs/simple-crud-bean-validation).