I have been trying validate a date attribute with java.util.Date class be means of javax.validation annotations.
I have found some annotation to do it, for instance: #Past, #PastOrPresent, #Future, #FutureOrPresent; however I need the validation will be with a specific date.
I am searching something like this:
#Future("01/01/2019")
Does someone know how can I do it?. I would like to do it with annotation existing (I don't like to do it with custom annotations).
Regards.
I was able to create the annotation of this way:
FutureCustom annotation
Interface:
#Target({FIELD, PARAMETER})
#Retention(RUNTIME)
#Constraint(validatedBy = FutureCustom.class)
public #interface FutureCustom {
public abstract String max() default "";
public abstract String message() default "Message";
public abstract Class<?>[] groups() default {};
public abstract Class<? extends Payload>[] payload() default {};
}
Class:
public class FutureCustomImpl implements ConstraintValidator<FutureCustom, Date> {
private String max;
#Override
public void initialize(FutureCustom constraintAnnotation) {
this.max = constraintAnnotation.max();
}
#Override
public boolean isValid(Date value, ConstraintValidatorContext context) {
//Validation using value and max like parameter in annotation
// The max attribute will always be a String, you have to convert it to Date.
}
}
Of this Way the annotation will be:
#FutureCustom(max="10/10/2010")
Related
I have the following controller:
public interface SaveController {
#PostMapping(value = "/save")
#ResponseStatus(code = HttpStatus.CREATED)
void save(#RequestBody #Valid SaveRequest saveRequest);
}
SaveRequest corresponds to:
public class SaveRequest {
#NotNull
private SaveType type;
private String name;
}
and SaveType:
public enum SaveType {
DAILY, WEEKLY, MONTHLY;
}
The controller does not receive the enum itself, but a camelCase String. I need to convert that String into the corresponding enum. For instance:
daily should become DAILY.
weekly should become WEEKLY.
monthly should become MONTHLY.
Any other String should become null.
I've tried using the Spring Converter class, which does not work when the enum is inside an object (at least I don't know how to make it work in such times).
I honestly don't know what else to try
https://www.baeldung.com/jackson-serialize-enums
This site should probably give you plenty of options.
Best is probably something like this:
public enum SaveType {
DAILY, WEEKLY, MONTHLY;
#JsonCreator
public static SaveType saveTypeforValue(String value) {
return SaveType.valueOf(value.toUpperCase());
}
}
What you require is to have custom annotation with a custom validation class for Enum.
javax.validation library doesn't have inbuilt support for enums.
Validation class
public class SaveTypeSubSetValidator implements ConstraintValidator<SaveTypeSubset, SaveType> {
private SaveType[] subset;
#Override
public void initialize(SaveTypeSubset constraint) {
this.subset = constraint.anyOf();
}
#Override
public boolean isValid(SaveType value, ConstraintValidatorContext context) {
return value == null || Arrays.asList(subset).contains(value);
}
}
interface for validation annotation with validation message
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = SaveTypeSubSetValidator.class)
public #interface SaveTypeSubset {
SaveType[] anyOf();
String message() default "must be any of {anyOf}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Usage
#SaveTypeSubset(anyOf = {SaveType.NEW, SaveType.OLD})
private SaveType SaveType;
This is one way. More ways are mentioned in this article.
Essentially, my service takes the role of streamlining the delivery of Email Notifications for standardization/control. I've therefore exposed a POST endpoint which takes in an email Bean as the response body which holds information such as receiver, sender, cc, etc and I would like to verify the fields of the incoming bean (i.e. email address format).
Currently, I have written a custom validator for validating a list of email addresses (#EmailAddresses). Is there a way to reuse the same validator to validate the email address for the "from" property which isn't a list as opposed to introducing another validator?
My Bean:
public class Email {
#JsonProperty("from")
private String from;
#EmailAddresses
#JsonProperty("to")
private List<String> to;
#EmailAddresses
#JsonProperty("cc")
private List<String> cc;
// some other fields
}
My Controller:
#RestController
public class MyController {
#RequestMapping(value = "/", method = RequestMethod.POST)
public String deliverEmailNotification(#Valid #RequestBody Email email) {
// something
}
}
My Custom Validation Annotation:
#Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = EmailAddressesValidator.class)
public #interface EmailAddresses {
String message() default "Must be a valid email";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validation Implementation:
public class EmailAddressesValidator implements
ConstraintValidator<EmailAddresses, List<String>> {
#Override
public void initialize(EmailAddresses emailAddresses) {
// do nothing
}
#Override
public boolean isValid(final List<String> emails, ConstraintValidatorContext constraintValidatorContext) {
// do something
}
}
So essentially I'm wondering whether is it possible to do something like this:
public class EmailAddressesValidator implements
ConstraintValidator<EmailAddresses, List<String>>, ConstraintValidator<EmailAddresses, String> {
#Override
public void initialize(EmailAddresses emailAddresses) {
// do nothing
}
#Override
public boolean isValid(final List<String> emails, ConstraintValidatorContext constraintValidatorContext) {
// do something
}
#Override
public boolean isValid(final String email, ConstraintValidatorContext constraintValidatorContext) {
// do something
}
}
Or is there another way around it?
Didn't manage to implement two instances of the ConstraintValidator due to Duplicate class error. However, I was able to achieve the equivalent of overloading by having the validation interface be validated by two implementation classes.
Based on the field type annotated with the validation annotation (#EmailAddress in this case), the respective validation implementation will kick in.
#Target({ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = { EmailAddressValidator.class, EmailAddressesValidator.class })
public #interface EmailAddress {
String message() default "Must be a valid email";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validation implementation for List of Strings
public class EmailAddressesValidator implements ConstraintValidator<EmailAddress, List<String>> {
...
}
Validation implementation for String
public class EmailAddressValidator implements ConstraintValidator<EmailAddress, String> {
...
}
Assume I have simple dto with one field and this dto is also annotated with custom validation annotation:
#CustomAnnotation
public class SimpleDto {
private String field;
}
// setters and getters omited
Custom annotation:
#Target(TYPE)
#Retention(RUNTIME)
#Constraint(validatedBy = CustomValidator.class)
#Documented
public #interface CheckMondialRelayShopOrderWeight {
String message() default "{temp.key.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and finally validator itself:
public class CustomValidator implements
ConstraintValidator<CustomAnnotation, SimpleDto> {
#Override
public void initialize(CustomAnnotation constraintAnnotation) {}
#Override
public boolean isValid(SimpleDto value,
ConstraintValidatorContext context) {
HibernateConstraintValidatorContext hibernateContext = context.unwrap(HibernateConstraintValidatorContext.class);
hibernateContext.addMessageParameter("dynamicValue", 130);
hibernateContext.buildConstraintViolationWithTemplate(hibernateContext.getDefaultConstraintMessageTemplate()).addPropertyNode("field").addConstraintViolation().disableDefaultConstraintViolation();
return false;
}
}
and in application.properties:
CustomAnnotation.simpleDto.field=Your dynamic value is {dynamicValue}
But this doesn't work, this works just fine if I put hibernateContext.buildConstraintViolationWithTemplate("Your dynamic value is {dynamicValue}").addConstraintViolation().disableDefaultConstraintViolation(); instead of hibernateContext.buildConstraintViolationWithTemplate(hibernateContext.getDefaultConstraintMessageTemplate()).addPropertyNode("field").addConstraintViolation().disableDefaultConstraintViolation();
I can't figure out how to use the interpolation with addPropertyNode. Any suggestions?
I'm trying to apply multiple common annotations at once with a custom validation annotation like this:
#Retention(RetentionPolicy.RUNTIME)
#Length(max=25, min=1, message="invalid length")
#NotNull
#Pattern(regexp = "[a-zA-Z0-9]{1, 25})")
public #interface MyAnnotation {
}
And using it in my model classes like this:
#MyAnnotation
public String firstName;
None of these validation are working, but they work as expected when used in the model class itself. I also tried registering MyAnnotation in the applications run method, and that didn't work either.
environment.jersey().register(MyAnnotation.class);
What else do I need to do in order to use custom validations?
Either annotate the String directly:
#Pattern(regexp = "[a-zA-Z0-9]{1, 25})")
#NotNull
#Length(max=25, min=1, message="invalid length")
public String firstName;
Or create a validator, something like:
class MyAnnotatationValidator implements ConstraintValidator<MyAnnotation, String>{
#Override
public void initialize(MyAnnotation a){}
#Override
public boolean isValid(String s, ConstraintValidationContext c) {
return s != null && (s.length() > 0 && s.length() < 26) && s.matches("[a-zA-Z0-9]{1, 25})";
}
}
And have
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy=MyAnnotatationValidator.class)
public #interface MyAnnotation {
String message() default = "{MyAnnotation}";
Class<?>[] groups() default {};
Class<? extends Payload> payload() deafult {};
}
According to the JSR-303
A constraint definition may have attributes that are specified at the time the constraint is applied to a JavaBean. The properties are mapped as annotation elements. The annotation element names message, groups and payload are considered reserved names; annotation elements starting with valid are not allowed; a constraint may use any other element name for its attributes.
So you must add message, groups and payload attributes to your #MyAnnotation.
A composition is done by annotating the composed constraint, example:
#Length(max=25, min=1, message="invalid length")
#NotNull
#Pattern(regexp = "[a-zA-Z0-9]{1, 25})")
#Documented
#Target({ANNOTATION_TYPE, METHOD, FIELD, CONSTRUCTOR, PARAMETER})
#Retention(RUNTIME)
public #interface MyAnnotation {
String message() default "My annotation message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Is there are any java annotation(s) that can validate like the example below?
String test;
test = null; //valid
test = ""; //invalid
test = " "; //invalid
test = "Some values"; //valid
You need to create a custom annotation: #NullOrNotBlank
First create the custom annotation: NullOrNotBlank.java
#Target( {ElementType.FIELD})
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = NullOrNotBlankValidator.class)
public #interface NullOrNotBlank {
String message() default "{javax.validation.constraints.NullOrNotBlank.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {};
}
Then the actual validator: NullOrNotBlankValidator.java
public class NullOrNotBlankValidator implements ConstraintValidator<NullOrNotBlank, String> {
public void initialize(NullOrNotBlank parameters) {
// Nothing to do here
}
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
return value == null || value.trim().length() > 0;
}
}
There isn't such an annotation in either javax.validation or Hibernate Validator. There was a request to add one to Hibernate Validator but it was closed as "won't fix" due to the possibility of writing your own relatively easily. The suggest solution was to either use your own annotation type defined like this:
#ConstraintComposition(OR)
#Null
#NotBlank
#ReportAsSingleViolation
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Constraint(validatedBy = { })
public #interface NullOrNotBlank {
String message() default "{org.hibernate.validator.constraints.NullOrNotBlank.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
or to use the #Pattern annotation with a regular expression that requires a non-whitespace character to be present (as the Pattern annotation accepts nulls and does not match them against the pattern).
Where is a nice javax.validation.constraints.Pattern annotation.
You can annotate the field with:
#Pattern(regexp = "^(?!\\s*$).+", message = "must not be blank")
This checks if field matches regex. The regex itself is something but not blank (see details here). It uses negative lookahead.
This is possible without creating a custom annotation, by using javax.validation.constraints.Size
// Null values are considered valid
#Size(min=1) String test;
The best way is to create your own constraint validator,
//custom annotation
#Documented
#Constraint(validatedBy = CustomCheck.class)
#Target( { ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface CustomConstarint {
String message() default "Invalid data";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
//validation logic goes here
public class CustomCheck implements
ConstraintValidator<CustomConstarint, String> {
#Override
public void initialize(CustomConstarint customConstarint) {
}
#Override
public boolean isValid(String field,
ConstraintValidatorContext cxt) {
//write your logic to validate the field
}
}
Did you try Hibernate-Validator? I think that's what you are looking for.
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
public class MyModel {
#NotNull
private String str1;
#NotEmpty
private String str2;
#NotBlank
private String str3;
}