Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Springboot - validate @RequestBody

Question: It is possible to validate the JSON payload of a request body, without specifically writing if statements? Maybe via annotation or configuration?

I have a very easy POJO:

public class Foo {

    private int important;
    private String something;

//constructors, getter, seters, toString
}

And a very easy controller class:

@SpringBootApplication
@RestController
public class QuestionController {

    public static void main(String[] args) {
        SpringApplication.run(QuestionController.class, args);
    }

    @GetMapping(value = "/question")
    Mono<String> question(@RequestBody Foo foo) {
        System.out.println("The object foo, with value for important = " + foo.getImportant() + " and something = " + foo.getSomething());
        return Mono.just("question");
    }

}

If I query with a payload such as:

{
    "important": 42,
    "something": "value"
}

Everything is working perfectly fine, very happy.

However, if there is a typo: (note the typo on "important")

{
    "importantWithTypo": 42,
    "something": "value"
}

Or the required "important" is absent (note the JSON is not even complete)

{
    "something": "value"
}

The request and computation are still valid! And the value of "important" is 0!

I do not want Spring to default to 0 and to thinks everything is fine.

I also do not want to change my types from primitives to boxed object.

Without me writing something like:

 @GetMapping(value = "/question")
    Mono<String> question(@RequestBody Foo foo) {
        if (0 == foo.getImportant()) {
            throw new IllegalArgumentException();
        }
        System.out.println("The object foo, with value for important = " + foo.getImportant() + " and something = " + foo.getSomething());
        return Mono.just("question");
    }

What is the most efficient way to resolve this? Some kind of annotation? Or maybe Spring boot configuration?

Thank you

like image 332
PatPatPat Avatar asked Oct 24 '20 20:10

PatPatPat


People also ask

How do you validate a RequestBody?

Spring offers an elegant way to validate the user input. The @RequestBody annotation is used to bind the HTTP request body with a domain object in the method parameter and also this annotation internally uses the HTTP Message converter to convert the body of the HTTP request to a domain object.

How do I validate a parameter in Spring boot?

Validating a RequestParam To do so, we'll use the @Min and @Max annotations: @GetMapping("/name-for-day") public String getNameOfDayByNumber(@RequestParam @Min(1) @Max(7) Integer dayOfWeek) { // ... } Any request that doesn't match these conditions will return an HTTP status 400 with a default error message.


2 Answers

Add @NotNull annotation on a field (you may need to change type to Integer), and add @Valid annotation on the method parameter of the controller.

Mono<String> question(@Valid @RequestBody Foo foo) {
    ...
}
public class Foo {

    @NotNull
    private Integer important;
    private String something;

//constructors, getter, seters, toString
}

You can find more information here: https://lmonkiewicz.medium.com/the-power-of-spring-rest-api-validation-77be83edef

like image 127
Łukasz Monkiewicz Avatar answered Oct 06 '22 07:10

Łukasz Monkiewicz


The already provided answer covers the answer.

However I would like to elaborate on one thing that you asked.

How to fail on this "importantWithTypo": 42,

2 aspects to it.

  1. you want to return a 4XX if a required field is not present (this can be achieved by the already given answer) -- @NonNull/@NonEmpty in conjunction with @Validate annotation
  2. You want to error out on presence of an unknown field importantWithTypo. This can be achieved by jackson's fail_on_unknown_properties property. (May be default is fail_on_unknown_properties = enabled, I havent checked so not sure).

Don't do this 2nd thing. That will make your 2 services tightly coupled. By doing this fail_on_unknown_properties = enabled, you are forfeiting the potential opportunity to enhance the consumer/caller service in a nonbreaking fashion. You will have to coordinate both the apps releases.

like image 42
so-random-dude Avatar answered Oct 06 '22 07:10

so-random-dude