8

I have a RESTful service which receives POST request with UUID values and writes them in DB. So the problem is to validate if UUID is valid or not. For this purpose I implemented custom annotation:

@Constraint(validatedBy = {})
@Target({ElementType.FIELD})
@Retention(RUNTIME)
@Pattern(regexp = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[34][0-9a-fA-F]{3}-[89ab][0-9a-fA-F]{3}-[0-9a-fA-F]{12}")
public @interface validUuid {

    String message() default "{invalid.uuid}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

But for some reason it doesn't work, even if I pass valid UUID I constantly get an exception:

javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.Pattern' validating type 'java.util.UUID'

Are there any options to validate UUID properly?

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
Abraksis
  • 91
  • 1
  • 6

2 Answers2

8

You cannot apply the @Pattern annotation to something (java.util.UUID) that is not a CharSequence. From the @Pattern annotation documentation (emphesizes mine):

Accepts CharSequence. null elements are considered valid.

Moreover, as far as I see you try to extend the behavior of the validation annotation handler by passing it to the new annotation definition.

If you wish to perform more complex validation, simply create your annotation without another validation annotations - their combining doesn't work like this. There must be something to recognize annotations and validate them.

@Target({ElementType.FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = UuidValidator.class)
public @interface ValidUuid {

    String message() default "{invalid.uuid}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

Now, create a validator which implements ConstraintValidator<ValidUuid, UUID> and override the methods performing the validation itself.

public class UuidValidator implements ConstraintValidator<ValidUuid, UUID> {
    private final String regex = "....." // the regex

    @Override
    public void initialize(ValidUuid validUuid) { }

    @Override
    public boolean isValid(UUID uuid, ConstraintValidatorContext cxt) {
        return uuid.toString().matches(this.regex);
    }
}

And apply the annotation:

@ValidUuid
private UUID uuId;
Mojtaba
  • 382
  • 1
  • 2
  • 26
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
  • 1
    Thanks for your reply! Maybe there is a problem, I don't use String I use UUID type: `@validUuid (message = "Please provide valid UUID")` `private UUID uuid;` – Abraksis Dec 20 '18 at 12:24
  • I have edited my answer. You cannot use `@Pattern` to `java.util.UUID`. – Nikolas Charalambidis Dec 20 '18 at 12:32
  • 2
    @NikolasCharalambidis it is not working unless you add `@Constraint(validatedBy = UuidValidator.class)` to the ValidUuid Interface – Mojtaba Jan 27 '21 at 10:03
  • 1
    @Mojtaba: That's true, I wonder how I could had missed it. Thanks for the contribution! – Nikolas Charalambidis Jan 27 '21 at 10:09
  • Not necessarily required to create a custom validator. You can compose the constraints as the question's example code exhibits. See https://docs.jboss.org/hibernate/validator/7.0/reference/en-US/html_single/#section-constraint-composition – juliaaano Jan 29 '21 at 11:31
  • @juliaaano: Since when is this feature available? I havent seen it before. – Nikolas Charalambidis Jan 29 '21 at 11:33
  • Not sure, but I found on Hibernate's documentation as early as version 4.x. There is this other answer from StackOverflow: https://stackoverflow.com/questions/37320870/is-there-a-uuid-validator-annotation – juliaaano Jan 29 '21 at 11:36
  • Ah right, I am sorry, I scrolled to ValueExtractor and I though it is somehow associated with the valudations somehow after skimming throught the articles. – Nikolas Charalambidis Jan 29 '21 at 11:41
0

you can use UUID.fromString(...); and catch IllegalArgumentException

myxaxa
  • 1,391
  • 1
  • 8
  • 7