Is there a way to Spring custom validator reject value without field having a getter method?

Viewed 342

I'm currently studying building API's with Spring. I'm working with Spring Validator to validate my input. Here it is my custom validator:

public class NewHoldValidator implements Validator {

    private EntityManager manager;

    public NewHoldValidator(EntityManager manager) {
        this.manager = manager;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return NewHoldRequest.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        if (errors.hasErrors()) {
            return;
        }

        NewHoldRequest request = (NewHoldRequest) target;

        Patron patron = manager.find(Patron.class, request.patronId);
        BookInstance bookInstance = manager.find(BookInstance.class, request.bookInstanceId);

        Assert.state(patron != null, "Patron does not exists.");
        Assert.state(bookInstance != null, "Book instance does not exists.");

        if (!bookInstance.acceptToBeHoldTo(patron)) {
            errors.reject(null, "This book instance cannot be hold to this patron");
        }

        if (!request.hasDaysHold()) {
            if (!patron.researcher()) {
                errors.rejectValue("daysHold", null, "You need to pass a daysHold attribute");
            }
        }
    }
}

And here is my NewHoldRequest class:

public class NewHoldRequest {
    @NotNull
    public final Long patronId;
    @NotNull
    public final Long bookInstanceId;
    @Positive
    @Max(60)
    public final Integer daysHold;

    public NewHoldRequest(@NotNull Long patronId, @NotNull Long bookInstanceId, @Positive @Max(60) Integer daysHold) {
        this.patronId = patronId;
        this.bookInstanceId = bookInstanceId;
        this.daysHold = daysHold;
    }

    @Override
    public String toString() {
        return "NewHoldRequest{" + "patronId=" + patronId + ", bookId=" + bookInstanceId + ", daysHold=" + daysHold + '}';
    }

    public boolean hasDaysHold() {
        return this.daysHold != null;
    }

Even if my field "daysHold" is public I still need to create a getter to it so Spring can show the rejected error properly, otherwise, it will throw a NotReadablePropertyException. Is there a way to define that Spring can reject public fields without getters or I will need to add accessor methods to all fields I want to reject?

Here is the message that shows up when my validation is triggered.

org.springframework.beans.NotReadablePropertyException: Invalid property 'daysHold' of bean class [com.api.library.usecases.newhold.NewHoldRequest]: Bean property 'daysHold' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
1 Answers

I'll advise you to use the interface ConstraintValidator. It's a generic's interface, without casting and other problems. You should create a custom constraint annotation for you it's @NewHoldValid after that implement CustomValidator interface:

@Component
public class NewHoldValidator implements ConstraintValidator<NewHoldValid, NewHoldRequest> {   

    @Override
    public boolean isValid(NewHoldRequest  value, ConstraintValidatorContext context) {
        if(ANY_CONDITION){
           return true;
       }else {
          return false;
       }
    }

After that use @NewHoldValid annotation above your NewHoldRequest. If you'll want to set an error message text which different from default use that:

context.buildConstraintViolationWithTemplate("Your error message").addConstraintViolation();
Related