Problem:
I need to validate some json inputs and outputs in my Spring API, I’m trying to use spring boot starter validation (Already know that is not into spring-boot-starter-web, so I added it manually) but it never throws the exception when something is bad based on the validations in the DTO class (MethodArgumentNotValidException)
Complete project can be fonud on github
What I’ve tried:
I tried adding this 3 dependencies:
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.13.Final</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.el</artifactId> <version>3.0.0</version> </dependency>
And is not working, I’m able to send incorrect values without throwing the exception
P.S. I already know that spring boot starter validation is not into spring-boot-starter-web, so I added it manually
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
If I try to use spring-boot-starter-validation without the validation-api (from javax) it directly says that some methods do not exists (Like @Valid or @Max or @Min)
Already Rebuild project
Already clean maven repository and installed again
What I have:
DTOs:
import lombok.*; import javax.validation.Valid; import javax.validation.constraints.*; import java.util.List; @Data @NoArgsConstructor @AllArgsConstructor public class StudentDTO { @NotEmpty(message = "Username cant be empty") @Size(min = 2, max = 50, message = "Minimum 2 letters, max 50") String studentName; String message; Double averageScore; List<@Valid SubjectDTO> subjects; }
import lombok.*; import javax.validation.constraints.*; @Data @NoArgsConstructor @AllArgsConstructor public class SubjectDTO { @NotEmpty(message = "Subject name cant be empty") @Size(max = 30, message = "Max 30 letters") @Pattern(regexp = "[A-Z](\p{L}||\s)+", message = "Name should start with capital letter") String name; @Max(value = 10, message = "Max grade is 10.0") @Min(value = 0, message = "Min grade is 0.0") Double score; }
import lombok.*; @Data @AllArgsConstructor @NoArgsConstructor public class ErrorDTO { private String name; private String description; }
Rest controller:
import com.practicastesting.diploma.dto.StudentDTO; import com.practicastesting.diploma.service.IObtenerDiplomaService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; @RestController public class ObtenerDiplomaController { @Autowired IObtenerDiplomaService service; @PostMapping("/analyzeScores") public ResponseEntity<StudentDTO> analyzeScores(@RequestBody @Valid StudentDTO rq) { return new ResponseEntity<StudentDTO>(service.analyzeScores(rq), HttpStatus.OK); } }
Exception controller:
import com.practicastesting.diploma.dto.ErrorDTO; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import java.util.Objects; @ControllerAdvice public class ExceptionController { @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseBody @ResponseStatus(value = HttpStatus.BAD_REQUEST) protected ResponseEntity<ErrorDTO> handleValidationExceptions(MethodArgumentNotValidException e) { ErrorDTO error = new ErrorDTO("MethodArgumentNotValidException", Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage()); return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); } }
Well, with that, when I try to send some json like:
{ "studentName": "a", "subjects": [ { "name": "Math", "score": 115 }, { "name": "Whatever", "score": 7 }, { "name": "Chemistry", "score": 6 } ] }
It should give me an error, telling me the message of each of the validations that are defined into the DTO, but is not giving me any error or exception, it just save it into the repository, and working normally.
More details:
I’m using:
- Spring boot 2.5.4
- Java 11
my pom.xml looks like this:
<?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.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.practicastesting</groupId> <artifactId>diploma</artifactId> <version>0.0.1-SNAPSHOT</version> <name>diploma</name> <description>Demo project for Spring Boot</description> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> <version>2.4.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.13.Final</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.el</artifactId> <version>3.0.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
If u need more details just ask, ill give them all
Advertisement
Answer
Hello @Sebastián Villegas, I downloaded your project from GitHub and proved it with the request that you added above and the program is doing the corresponding validation (result that I obtained bellow):
{ "name": "MethodArgumentNotValidException", "description": "Minimum 2 letters, max 50" }
Rebuild your project. If you are using IntelliJ IDEA as your IDE, in the menu go to Build -> Rebuild Project