简体   繁体   中英

Annotation based validation on DTO level happens before field level validation like @NotNull @Size etc

I have a DTO class where I made some fields mandatory. And, based on another type field where value can be assume A & B if type A, need to check only 1 item can be passed, if B it can be more than 1 also.

I need to check if a list has at least 1 value in DTO class level. And, in custom annotation validation I need to check size of list based on type field of DTO.

So, in DTO

@NotNull
@Size(min = 1)
private List<@NotBlank String> items;

And, inside annotation

        if (cancelType == CancellationTypeEnum.A && cancelDto.getItems().size() > 1) {

            isValid = false;
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(
                    "Only one item can be sent for 'A' cancel type.").addConstraintViolation();

        } 

But, since field level happens later, if I pass null for items field, it goes to this annotation validator & throw NPE as field is null & I am trying to get size

Temporary solution is I do null check, then check size, but in field level anyway we are giving @NotNull.

Is there any way to do class level custom annotation to validate after field level validation happens. In that case, it will throw field level validation as field is null & will not go to custom class level annotation

You could use JS-303 validation groups (see here ), in conjunction with @Validated , from the documentation:

Variant of JSR-303's Valid, supporting the specification of validation groups.

For example:

@CheckEnumSize(groups = {Secondary.class})
public class CancelDto {

    public CancelDto(List<String> items) {
        this.items = items;
    }

    @NotNull(groups = {Primary.class})
    @Size(min = 1)
    public List<@NotNull String> items;

    public CancellationTypeEnum cancelType;

    public List<String> getItems() {
        return items;
    }
}
 

where Primary is a simple interface:

public interface Primary {
}

and the same for Secondary :

public interface Secondary {
}

finally you could use validated as follows:

@Service
@Validated({Primary.class, Secondary.class})
public class CancelService {

    public void applyCancel(@Valid CancelDto cancel) {
        //TODO
    }

}

when using the above code with a CancelDto with items null, you should get:

Caused by: javax.validation.ConstraintViolationException: applyCancel.cancel.items: must not be null

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM