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?
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;
you can use UUID.fromString(...); and catch IllegalArgumentException
Related
I am using Spring Boot validation to validate some fields.
I've created the ValidationMessages.properties file under resources and overwritten the value for the default validation of Size as follows:
javax.validation.constraints.Size.message=Please enter a value. Maximum length is {max}.
Which works like a charm, at runtime the {max} token is replaced with the value from the annotation #Size(max = 100).
Now I want to define a custom proprietary entry, something like:
my.custom.message=Hey, my custom value is {customValue}
The question is - how can I replace the {customValue} token at runtime starting from something like?
private static final String CUSTOM_STRING = "{my.custom.message}";
In the context of validation, you must use something like:
#Documented
#Constraint(validatedBy = MyValidator.class)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation
{
int customValue() default "THIS IS CUSTOM"; // this will be the value that will be injected in the custom message,
// and can be changed when using the annotation
String message() default "{my.custom.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Also, if you use
private static final String CUSTOM_STRING = "{my.custom.message}";
in the context of validation, the replacement will be done.
I'm wondering if it's possible to do something like:
#Min(1)
#Max(100)
public #interface ValidationForX {}
and then
#ValidationForX
int X;
For some reason #Min and #Max are applicable on annotations so I'm assuming it should be possible
I want to hide this validation behind one annotation because I want to reuse it
Thanks for your help!
You annotation must look like this:
#Min(1)
#Max(100)
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = {})
public #interface ValidationForX {
String message() default "value should be greater or equal than 1 and less or equal than 100.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Read more about composing constraints here: https://www.baeldung.com/java-bean-validation-constraint-composition
You can directly use #Min and #Max annotation on top of the field which you want to validate.
Something like this:
#Max(value=100)
#Min(value=1)
int X;
So I have created a custom constraint validator annotation like so:
#Documented
#Constraint(validatedBy = PostcodeValidator.class)
#Target( { ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface Postcode {
String message() default "not a well-formed postcode";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and I wanted to use it in combination with others like so:
#NotEmpty
#Postcode
private final String postcode;
But when attempting to test this I get the following:
java.lang.IllegalStateException: Duplicate key not a well-formed postcode
I was expecting it to fail the #NotEmpty constraint.
I know I can use composition to include one constraint within the other, but my question is, is it possible to use them as I am? And if so, what am I doing wrong?
Cheers,
I have a Jersey Rest API like this:
#POST
#Path("/doorder")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces("text/plain")
public String doOrder(#BeanParam final #Valid OrderBean order) {
// Some implementation here
}
All my inputs are store in this bean:
#AddressAtLeastOne
public final class OrderBean {
#FormDataParam("address")
private String address;
#FormDataParam("city")
private String city;
#FormDataParam("postcode")
private String postcode;
// Other member variables
// Getters and setters
}
I added an annotation to validate the address (#AddressAtLeastOne). The address is valid if at least one of the 3 fields has a value.
Here's the annotation definition:
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
#Retention(RUNTIME)
#Constraint(validatedBy = AddressAtLeastOneValidator.class)
#Documented
public #interface AddressAtLeastOne {
String message() default "Address requires at least one field";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And here's the validator:
public class AddressAtLeastOneValidator implements ConstraintValidator<AddressAllOrNone, OrderBean> {
#Override
public boolean isValid(OrderBean demoBean, ConstraintValidatorContext constraintValidatorContext) {
// Check for at least one value
if((demoBean.getAddress() != null && !demoBean.getAddress().equals("") ||
(demoBean.getCity() != null && !demoBean.getCity().equals("")) ||
(demoBean.getPostcode() != null && !demoBean.getPostcode().equals("")))) {
return true;
}
return false;
}
}
Everything is fine! But now I want to rename the annotation #AddressAtLeastOne to #AtLeastOne and make it generic, so that I can apply it to any class. I need a mechanism where I can specify which member variables are part of the group I want to validate with #AtLeastOne. How can I do that?
One approach of doing this is to use Reflection -
Create a custom annotation suppose #GroupNotNullField and apply this annotation to all fields in bean class in which at least one field should have value. By this way, you can skip some fields in which validation is not required.
In the validator class, get all the fields of the bean class using Reflection
Check all the fields which are annotated with #GroupNotNullField annotation
Get the value of all such fields and check that at least one has value.
Return true or false depending on validation check.
How do I display the field name description in the constraint violation message of a Bean Validation 1.1 (JSR-349) custom constraint annotation?
For example, given the following custom constraint annotation #Required, resource bundle ValidationMessages.properties, and class Person, how can I compose the constraint violation message "First Name is required." for required field firstName and "Last Name is required." for required field lastName?
#Documented
#Constraint(validatedBy = {})
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#NotEmpty
#ReportAsSingleViolation
public #interface Required {
public abstract Class<?>[] groups() default {};
public abstract String message() default "{Required.message}";
public abstract Class<? extends Payload>[] payload() default {};
}
In resource bundle, ValidationMessages.properties:
Required.message=is required.
Class Person:
public class Person {
#Required
private String firstName;
#Required
private String lastName;
}
ConstraintViolation constraintViolation ..
((PathImpl)constraintViolation.getPropertyPath()).getLeafNode().getName();
There is no API you can use to get the current property name. And if there were, you still would do some string manipulation to get from the property name 'firstName' to the display name "First Name".
That said, I can see the benefit of exposing the current Path in ConstraintValidatorContext which is passed to ConstraintValidator#isValid. This is per specification not possible atm, but it could be implemented as a provider specific feature. You could create a issue request for Hibernate Validator here.
Regarding your problem, the best solution imo is to add a 'labelattribute to#Required`:
public class Person {
#Required(label="First Name"
private String firstName;
#Required(label="Last Name"
private String lastName;
}
Then you can interpolate the label in the message bundle like so:
Required.message={label} is required.
The constraint would look something like this
#Documented
#Constraint(validatedBy = {})
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#NotEmpty
#ReportAsSingleViolation
public #interface Required {
public abstract String label();
public abstract Class<?>[] groups() default {};
public abstract String message() default "{Required.message}";
public abstract Class<? extends Payload>[] payload() default {};
}
Note, you can add parameters you like (provided the parameter type is supported by Java).