I find myself in the following scenario, in a RestEasy
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 RestEasy
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-RestEasy parts of the code.
Follow-up question : Is it reasonable to have two different competing validation systems? (RestEasy's validation, plus another kind such as Jakarta's validation )
This could be a solution : How to manually trigger Spring validation?
1. Inject a validator
@Autowired
private Validator validator;
2. Use it manually
validator.validate(myObject);
The validator can be the default validator :
// JSR-303 Validator
import javax.validation.Validator;
...or some fancy validator, for example the validator provided by Spring :
// Spring Validator
import org.springframework.validation.Validator;