Reputation: 812
I am trying to validate a simple request body annotated by @Valid annotation in a @RestController annotated by @Validated. Validations are working correctly on primitive variable in request (on age in below example) but not working on pojo request body. @Valid annotation has no effect on request body Person class (i.e. controller accepting blank name and age under 18).
Person class:
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
class Person (
@NotBlank
val name : String,
@Min(18)
val age : Int
)
Controller class:
import org.springframework.validation.annotation.Validated
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
@RestController
@Validated
class HomeController {
@GetMapping("/{age}")
fun verifyAge(@PathVariable("age") @Min(18) age:Int): String {
return "Eligible age."
}
@PostMapping
fun personValidation(@Valid @RequestBody person : Person) : String {
return "No validation error"
}
}
@NotBlank, @Min and @Valid annotations came from below dependency:
implementation("org.springframework.boot:spring-boot-starter-validation:2.3.0.RELEASE")
How to make @Valid working on Person request body in a @Validated controller?
Upvotes: 14
Views: 26837
Reputation: 19
Please add this dependency to your pom.xml file.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Upvotes: 2
Reputation: 326
Don't mark Person as a Spring @Component.
Correct way to achieve field validation is to add @field to your properties. Example code:
class SomeRequest(
@field:NotBlank val name: String
)
This happens because Spring Boot needs this annotations to be applied on fields. Kotlin by default applied them to constructor parameter. See Annotation use-site targets on Kotlin documentation.
Upvotes: 19
Reputation: 812
[Edited] This also worked for me (adding field annotation on constructor arguments) as answered by one of the user:
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
class Person (
@field:NotBlank
val name : String,
@field:Min(18)
val age : Int
)
Though changing the "kotlin" class definition of Person as below (constructor arguments to no-arguments) (note parentheses (...) to curly-brackets{...}) also worked without putting @field annotaion:
import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank
class Person {
@NotBlank
lateinit var name: String
@Min(18)
var age: Int = 0
}
Upvotes: 4
Reputation: 432
Piotr Solarski is correct (above) that it is not right to add component to the request boddy as the body will change and the creating a single object for it can become tricky later (even if it resolved the problem for OP).
In an MVC application, here's what was going wrong for me.
I added the usual stuff you have done (libraries in pom, @Valid on RequestBody etc)
What the Spring docs (and many blogs) leave it as subtle is that Spring looks for an exit point to throw the error but if that exit doesn't exist, it will reply with 404. After reading a lot especially this blog, adding this class got Spring to recognize @Valid and find an exit point to throw the error
@RestControllerAdvice
@Order(1)
public class RestControllerExceptionHandler {
@RequestMapping(value = { "error/404" }, method = RequestMethod.GET)
@ExceptionHandler(Exception.class)
public String handleUnexpectedException(Exception e) {
return "views/base/rest-error";
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public String handlemethodArgumentNotValid(MethodArgumentNotValidException exception) { //
// TODO you can choose to return your custom object here, which will then get transformed to json/xml etc.
return "Sorry, that was not quite right: " + exception.getMessage();
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public String handleConstraintViolation(ConstraintViolationException exception) { //
// TODO you can choose to return your custom object here, which will then get transformed to json/xml etc.
return "Sorry, that was not quite right: " + exception.getMessage();
}
}
Upvotes: 0
Reputation: 3388
Just in case if somebody is using @BasePathAwareController
or @RepositoryRestController
annotations. @Valid
is not currently supported on @RepositoryRestController
or @BasePathAwareController
.
There is an open issue in Spring Data REST: https://github.com/spring-projects/spring-data-rest/issues/967
Upvotes: 1
Reputation: 371
In my case in order for @Valid @RequestBody to work in the springboot application it was necessary to use both dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
Upvotes: 18
Reputation: 46
You may try this option.
Please map @PostMapping with a URL like @PostMapping("/validateBody") and try with its URL. As done on https://reflectoring.io/bean-validation-with-spring-boot/
Upvotes: 0