javaspring-boothibernate-validator

@Valid annotation to validate outgoing (not incoming) request body payload body


I am trying to use the @Valid annotation to validate outgoing, (not incoming) request payload body.

There is this construct to validate incoming request's body payload in Spring Boot:

@RestController
class FieldValidationController {

    @PostMapping("/validate")
    String question(@Valid @RequestBody SomeRequest someRequest) {
        return "please validate the field";
    }

}
public record SomeRequest(@Email String email) { }

This would ensure that an incoming request to this web API would be validated (in this case, that the incoming request's email field is indeed an email).

Is it possible to do the same, not for incoming, but for outgoing requests?

With the same construct, I did this:

@HttpExchange(accept = "application/json")
public interface FooHttpExchange {

    @PostExchange("/outgoing")
    String question(@RequestBody @Valid final OutgoingFoo foo);

}

public record OutgoingFoo(
       int score,
        @Email
        String email
) {
}

And when trying to send the request, I do this:

String s = fooHttpExchange.question(foo);
OutgoingFoo foo = new OutgoingFoo(42, "thisisnotanemailpleasefaildontsendtherequest")

As you can see in the outgoing object, this is violating the rule.

I would have expected this to fail before sending the request.

I understand I could have written some custom if statements after the construction of my outgoing object. And this is not a question about email specifically, but the @Valid annotation in general.

How to use it for outgoing (not incoming) request validation?


Solution

  • Don't forget to add the @Validated annotation to the target classes:

    I will explain Spring's requirements for bean-validation and possible restrictions for HTTP interfaces below.

    Bean validation in Spring

    According to the Spring Framework reference documentation on Java Bean Validation, section Spring-driven Method Validation:

    To be eligible for Spring-driven method validation, target classes need to be annotated with Spring’s @Validated annotation, which can optionally also declare the validation groups to use.

    This annotation @Validated should be used for example in your controller class, or like given in the example of section "Customizing Validation Errors" in the same docs, at the service class:

    record Person(@Size(min = 1, max = 10) String name) {
    }
    
    @Validated
    public class MyService {
    
        void addStudent(@Valid Person person, @Max(2) int degrees) {
            // ...
        }
    }
    

    Note: Those examples are annotated classes, not interfaces. This could make a difference.

    HTTP Interface annotated with @HttpExchange

    In Spring Frameworks's HTTP Interface, i.e. Jave interfaces annotated with @HttpExchange, same bean-validation logic would make sense. But section 1.3.1. Method Parameters the annotation @Valid is not listed, although it could be applied to any parameter, theoretically.

    Still, stictly speaking, then the requirement for Spring's bean-validation as given above:

    target classes need to be annotated with Spring’s @Validated annotation

    can probably not be applied to an interface itself. But, it should work on a class if this class implements the HTTP interface, i.e. the interface annotated with @HttpExchange.