I find myself in the following scenario, in a Spring Boot
REST API:
@POST
@Path("my-endpoint")
@Produces(MediaType.APPLICATION_JSON)
public Response myEndpoint(
final @Nullable Payload payload
) {
final var result = runCommand(
new Command(payload),
withPermissions(CAN_WRITE)
);
return ok(result);
}
…
@Value
public class Payload {
@NotNull Long mSomeValidLong;
}
…
@Value
public class Command {
@NotNull @Valid mPayload;
}
As you can see, the endpoint does not validate the payload immediately.
Instead, there is nested logic : the payload is passed to a Command
which is then executed by method runCommand
.
Even though you can’t see how runCommand
is implemented, you can see that first it checks the permissions. It also has some fancy logic for worker threads and exception catching.
Bottom line : It is the Command that must validate the parameters, (because that’s when we’ve reached the place where the permissions have been controlled and when we’re sure we have the resources for that).
Don’t ask me why the permissions are not validated as an @Annotation immediately above the endpoint method, this is legacy code.
With that setup, I observe this :
This works (because Spring Boot
identifies the method as one of his) :
public Response myEndpoint(final @Valid @NotNull Payload payload) { ... }
This does not work because this is plain old Java :
public Command(final @Valid @NotNull Payload payload) { ... } // Constructor as it would be generated by Lombok
My question : is there any way at all to make @Valid do its magic somewhere else than in the endpoint’s method parameters?
From what I read, jakarta.validation
would do exactly the same kind of validation in non-Spring parts of the code.
Follow-up question : Is it reasonable to have two different competing validation systems? (Spring’s validation, and Jakarta’s validation )