Search code examples
javaspringspring-bootvalidation

Why my model is still passing validations on empty field to the db?


some strange issue has shown up, in the business model I use the list of addresses that cannot be empty or null, but each field in the object address cannot be null or empty either. But when I send a request with one of the fields empty, the request will pass to the repository, which shouldn't happen. Instead of passing a request, the server should return an internal server error.

Address object:

@Data
@NoArgsConstructor
public class Address {
    @NotEmpty(message = "Street may not be empty")
    @Size(min = 2, max = 256)
    private String street;

    @NotEmpty(message = "Number may not be empty")
    @Size(min = 1, max = 5)
    private String number;

    @NotEmpty(message = "Postal code may not be empty")
    @Field("postal_code")
    @JsonProperty("postal_code")
    @Size(min = 5, max = 6)
    private String postalCode;

    @NotEmpty(message = "City may not be empty")
    @Size(min = 2, max = 256)
    private String city;

    @NotEmpty(message = "Province may not be empty")
    @Size(min = 2, max = 256)
    private String province;

    @NotEmpty(message = "Country may not be empty")
    @Size(min = 2, max = 256)
    private String country;
}

Business object which contains list of addresses:

@Data
@NoArgsConstructor
@Document(collection = "businesses")
public class Business {
    @Id
    private String id;

    @NotBlank
    @Size(max = 64)
    private String name;

    @Field("created_at")
    private Date createdAt;

    @Field("updated_at")
    private Date updatedAt;

    private Boolean active;

    @DBRef
    private User owner;

    @Valid
    private List<Address> addresses;

    public Business(String name, User owner, List<Address> addresses, Date createdAt, Date updatedAt, Boolean active) {
        this.name = name;
        this.owner = owner;
        this.addresses = addresses;
        this.createdAt = createdAt;
        this.updatedAt = updatedAt;
        this.active = active;
    }

    public Business(String name, User owner, List<Address> addresses, Date updatedAt, Boolean active) {
        this.name = name;
        this.owner = owner;
        this.addresses = addresses;
        this.updatedAt = updatedAt;
        this.active = active;
    }
}

And in the end, service:

@Override
public ResponseEntity<?> createBusiness(CreateBusinessRequest request, String token) throws RuntimeException {
    Optional<User> user = userRepository.findByUsername(jwtUtils.getUserNameFromJwtToken(token.substring(7)));
    if(businessRepository.existsByName(request.getName())) {
        throw new ItemAlreadyExistsException("Business with " + request.getName() + " already exists!");
    }

    if(user.isEmpty()) {
        throw new NotFoundException("User with "+ user.get().getUsername() +" does not exist!");
    }

    if(request.getAddresses().isEmpty() || request.getAddresses() == null) {
        throw new InvalidInputException("Address is empty!");
    }

    Business business = new Business(
            request.getName(),
            user.get(),
            request.getAddresses(),
            new Date(),
            new Date(),
            true
    );

    businessRepository.save(business);
    return ResponseEntity.ok(business);
}

Controller:

@PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
@PostMapping("/create")
public ResponseEntity<?> createBusiness(@Valid @RequestBody CreateBusinessRequest request, @RequestHeader (name="Authorization") String token) {
    return businessService.createBusiness(request, token);
}

CreateBusinessRequest request model

@Data
public class CreateBusinessRequest {
    @NotNull
    private String name;

    @Valid
    private List<Address> addresses;
}

Request:

{
    "name": "Test business",
    "addresses": [
        {
            "street": "",
            "number": "100",
            "postal_code": "84104",
            "city": "Test city",
            "province": "Test",
            "country": "Slovakia"
        }
    ]
}

Pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ratify</groupId>
    <artifactId>backend</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>backend</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <json.webtoken.version>0.9.1</json.webtoken.version>
        <lombok.version>1.18.22</lombok.version>
        <jakarta.xml.bind.version>2.3.2</jakarta.xml.bind.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${json.webtoken.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <dependency>
            <groupId>jakarta.xml.bind</groupId>
            <artifactId>jakarta.xml.bind-api</artifactId>
            <version>${jakarta.xml.bind.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

I hope that I explained my issue correctly. Is there any solution to this issue?

Thanks in advance.


Solution

  • My guess is that you are missing one key dependency:

    <dependency> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-validation</artifactId> 
    </dependency>