Manishoaham
Manishoaham

Reputation: 812

Spring boot @Valid on requestBody in controller method not working

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

Answers (7)

Gihan Gunarathne
Gihan Gunarathne

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

Piotr Solarski
Piotr Solarski

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

Manishoaham
Manishoaham

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

veritas
veritas

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

Eugene Maysyuk
Eugene Maysyuk

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

Valeriy Emelyanov
Valeriy Emelyanov

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

agprogramming
agprogramming

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

Related Questions