@Valid not working for spring rest controller
Asked Answered
N

4

6

I have defined a rest endpoint method as:

@GetMapping("/get")
public ResponseEntity getObject(@Valid MyObject myObject){....}

This maps request parameters to MyObject.

MyObject is defined as(with lombok, javax.validation annotations):

@Value
@AllArgsConstructor
public class MyObject {

    @Min(-180) @Max(180)
    private double x;

    @Min(-90) @Max(90)
    private double y;

}

But validations are not working. Even with values out of prescribed range, request doesn't throw error and goes well.

Nebula answered 5/6, 2020 at 17:43 Comment(2)
Did you find your answer ? We have the same problem.Erie
Is this a regular @RestController or some Spring Data controller? There is some discussion below on @RestController vs @RepositoryRestController and different ways to get validation working there.Earth
L
7

If you on a version of Spring Boot > 2.3 it now states

Validation Starter no longer included in web starters

... you’ll need to add the starter yourself.

i.e.

For Maven builds, you can do that with the following:

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

For Gradle, you will need to add something like this:

dependencies {
  ...
  implementation 'org.springframework.boot:spring-boot-starter-validation'
}

Please refer to https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#validation-starter-no-longer-included-in-web-starters

Laborsaving answered 22/1, 2021 at 12:45 Comment(0)
L
3

Annotate your controller with org.springframework.validation.annotation.Validated

Lempira answered 4/7, 2020 at 5:22 Comment(6)
It's not true anwer. You can seen difference here #36173832Almena
This is absolutely correct. It just worked for me. Simply annotating the controller method parameter with @Valid did not work. I had to add @Validated at the controller class level. In my case this was Spring Data @RepositoryRestController.Earth
Looks like both options are correct, @Hett, depending on the controller annotation. When it's annotated with @RestController, @Validated is not necessary at the class level to get validation to work.Earth
@PawelZieminski in the questing the annotation @Valid already presented and this answer does not help solve the problem.Almena
In that case the OP is probably not using a vanilla @RestController then. It would be good to have this detail explicit in the question. With Spring Data controllers I had to add @Validated otherwise validation did not work.Earth
@Validated does not help, at least in my case. Adding the latest version of Spring Boot Starter Validation to my project fixed it though.Rabush
S
1

I see a couple of things here that you should fix. Let's start talking about the REST standard, the first rule is to think in endpoints as representation of resources, not operations, for example, in your code, I presume the MyObject class represents a Point (you should refactor the class to have a proper name), then the path value for the getObject can be "/point". The operations are mapped on the HTTP method, accordingly:

  • GET: Obtain info about a resource.
  • POST: Create a resource.
  • PUT: Update a resource.
  • DELETE: Delete a resource.

In getObject you're expecting to receive an object. The get method according to the REST standards means you want to retrieve some data, and usually you send some data included in the url like ../app-context/get/{id}, here the id is a parameter that tells your controller you want some info belonging to an id, so if you would invoke the endpoint like as ../app-context/get/1 to get info of some domain object identified by the number 1.

If you want to send data to the server, the most common HTTP method is a POST.

According to this, at design level you should:

  • Give a meaningful name to the MyObject class.
  • Check the operation you want to make in the getObject.
  • Assign a path to getObject representing a resource.

At code level, with the above comments, you could change this as:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyObject {

  @Min(-180) @Max(180)
  private double x;

  @Min(-90) @Max(90)
  private double y;
}

@PostMapping("/point")
public ResponseEntity savePoint(@RequestBody @Valid MyObject myObject) {...}

I will explain the changes:

  • Add @PostMapping to fulfill the REST standard.
  • Add @RequestBody, this annotation take the info sent to the server and use it to create a MyObject object.
  • Add @NoArgsConstructor to MyObject, by default, the deserialisation use a default constructor (with no arguments). You could write some specialised code to make the things work without the default constructor, but thats up to you.
Stutsman answered 7/6, 2020 at 23:29 Comment(0)
C
1

I just had to add the following dependency to get the validations working.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
Contortionist answered 1/5, 2021 at 18:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.