Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

ConstraintValidator throws both default and custom error in BindingResult

I have some code here with a ConstraintValidator that validates an object by comparing two of its attributes.

Here is the object, the annotation that is of interest to us is @ValidSmsTextLength:

@ValidSmsTextLength(groups = { PostGroup.class, PatchGroup.class, PostMessageCampaignGroup.class })
@JsonDeserialize(as = SmsMessageDto.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"text", "encoding", "messagePartCount", "length"})
public class SmsMessageDto extends AbstractRestDto implements OneOfMessage {

  @NotEmpty(message = "SMS_TEXT_NULL_OR_EMPTY", groups = { PostGroup.class, PatchGroup.class })
  @JsonProperty("text")
  private String text = null;

  @ValidParameterByEnum(enumValid = EncodingEnum.class, message = "INVALID_ENCODING_ENUM", groups = {PostGroup.class, PostMessageCampaignGroup.class})
  @JsonProperty("encoding")
  private EncodingEnum encoding = EncodingEnum.GSM7;

This object SmsMessageDto, which implements OneOfMessage, is an attribute (named "body") of that object:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

public class AbstractMessageDto extends AbstractRestResourceDto {

    @JsonDeserialize(using = OneOfMessageDtoDeserializer.class)
    @Valid
    @NotNull(message = "MESSAGE_NULL", groups = PostGroup.class)
    @JsonProperty("body")
    protected OneOfMessage body = null;

And here are the Interface and ConstraintValidator associated with the annotation @ValidSmsTextLength:

@Constraint(validatedBy = SmsTextLengthValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidSmsTextLength {

    String message() default "DEFAULT_SMS_TEXT_LENGTH_MESSAGE";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
public class SmsTextLengthValidator implements ConstraintValidator<ValidSmsTextLength, SmsMessageDto> {

    private static final String TEXT = "text";

    @Override
    public boolean isValid(SmsMessageDto smsMessageDto, ConstraintValidatorContext constraintValidatorContext) {

        EncodingEnum encodingEnum = smsMessageDto.getEncoding();
        if (smsMessageDto.getText() != null && EncodingEnum.GSM7.equals(encodingEnum) && smsMessageDto.getText().length() > 1530) {
            constraintValidatorContext
                    .buildConstraintViolationWithTemplate("SMS_TEXT_LENGTH_GSM7_ERROR")
                    .addPropertyNode(TEXT)
                    .addConstraintViolation();
            return false;
        } else if (smsMessageDto.getText() != null && EncodingEnum.UNICODE.equals(encodingEnum) && smsMessageDto.getText().length() > 670) {
            constraintValidatorContext
                    .buildConstraintViolationWithTemplate("SMS_TEXT_LENGTH_UNICODE_ERROR")
                    .addPropertyNode(TEXT)
                    .addConstraintViolation();
            return false;
        }
        return true;
    }
}

I implemented a test where I validate (through a @Validated annotated controller method) a AbstractMessageDto object which has a text attribute that satisfy the first if() condition in the isValid() method.

The BindingResult object present in my controller method ends up containing two errors:

  • one that has been created with the default message ("DEFAULT_SMS_TEXT_LENGTH_MESSAGE"), where the field is "body".
  • and one created with the correct isValid() message ("SMS_TEXT_LENGTH_GSM7_ERROR" for my test), where the field is "body.text" (because of the addPropertyNode(TEXT) in isValid()).

I would like for my BindingResult to not contain the error initialized with the default message. How can I stop the ConstraintValidator from creating that error and adding it to the BindingResult object?

>Solution :

You need to call context.disableDefaultConstraintViolation().

One more suggestion for your validator: if the smsMessageDto is null, you will get a NullPointerException, because the @NotNull hasn’t caused an error yet (the validation is still taking place). Add the following as first lines of the method:

if (smsMessageDto == null) {
    // handled by @NotNull
    return true;
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading