Given a username field which cannot be null, must be between 4 and 64 characters long and match the regexp [A-Za-z0-9]+, when the username field is null, the error message is simply: must not be null. The desired outcome is must not be null AND length must be between 4 and 64 characters AND must match "[A-Za-z0-9]+".
Initial setup:
#NotNull
#Length(min = 4, max = 64)
#Pattern(regexp = "[A-Za-z0-9]+")
String username;
What I also tried:
#NotNull(message = """
{jakarta.validation.constraints.NotNull.message}
AND {org.hibernate.validator.constraints.Length.message}
AND {jakarta.validation.constraints.Pattern.message}""")
#Length(min = 4, max = 64)
#Pattern(regexp = "[A-Za-z0-9]+")
String username;
But this latter's outcome is:
ConstraintViolationImpl{
interpolatedMessage='must not be null AND length must be between {min} and {max} AND must match "{regexp}"',
propertyPath=username, rootBeanClass=class app.User,
messageTemplate='{jakarta.validation.constraints.NotNull.message} AND {org.hibernate.validator.constraints.Length.message} AND {jakarta.validation.constraints.Pattern.message}'}
The values for the constraints (min, max, regexp) are not accessed. How to render the actual values of these in the error message?
Most constraint annotations, including #Length and #Pattern, regard null as valid input. That's why you won't get what you want by just using these annotations.
Fortunately, it's really easy to do what you want by introducing a new constraint annotation:
#Constraint(validatedBy = {}) // no validator needed, it delegates to other constraints
#NotNull
#Length(min = 4, max = 64)
#Pattern(regexp = "[A-Za-z0-9]+")
#ReportAsSingleViolation // to prevent separate violations for the other constraints
#Target(FIELD)
#Retention(RUNTIME)
public #interface ValidUsername {
String message() default """
{jakarta.validation.constraints.NotNull.message}
AND {org.hibernate.validator.constraints.Length.message}
AND {jakarta.validation.constraints.Pattern.message}""";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
This is annotated with your constraints, which means these are applied when this annotation is checked.
The message will still contain place holders {min} etc., because these are not properties of this constraint itself. You can solve that in two ways:
Don't use the templates but hard-code the message
Add these properties to your annotation as well, and tell the Bean Validation framework to use these for the other annotations:
#OverridesAttribute(constraint = Length.class)
int min() default 4;
#OverridesAttribute(constraint = Length.class)
int max() default 64;
#OverridesAttribute(constraint = Pattern.class)
String regexp() default "[A-Za-z0-9]+";
Now all you need to do is replace the annotations on the username field with #ValidUsername.
Related
I am using bean validation specification to validate my form on spring-boot thymeleaf project.
My entity property is as follow.
#NotEmpty(message = "{Password should not be empty}")
#Pattern(regexp = //Pattern for range 1-20, message = "{Wrong Input}")
private String password;
When I run and inputed to password field of my form with empty value, both of Validation Error Messages were shown.
My expectation is, while empty value is inputed, only #NotEmpty annotation should work and on the other hand, only #Pattern should be shown upon user input is wrong.
How can I do with Bean Validation Specification for that?
Regards.
1. Validation groups
#NotEmpty(groups = First.class), message = ...,
#Pattern(groups = Second.class, regexp = ...)
private String password;
Create the validation groups:
//Validation Groups - Just empty interface used as Group identifier
public interface First {
}
public interface Second {
}
and validate the model this way:
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Model>> violations = validator.validate(model, First.class);
if(violations.isEmpty()){
violations = validator.validate(model, Second.class);
}
2. Groups Sequences
I've never used them, but it seems it does just what you want
Check this other answer (https://stackoverflow.com/a/7779681/641627). I've added below a quote from this answer (from #Gunnar), which ironically also uses First and Second group names:
#GroupSequence({First.class, Second.class})
public interface Sequence {}
#Size(min = 2, max = 10, message = "Name length improper", groups = { First.class })
#Pattern(regexp = "T.*", message = "Name doesn't start with T" , groups = { Second.class })
private String name;
When now validating a Bean instance using the defined sequence
(validator.validate(bean, Sequence.class)), at first the #Size
constraint will be validated and only if that succeeds the #Pattern
constraint.
With this solution, you wouldn't need to manually call validator.validate(...), the validations would be performed in the order defined in the Sequence with short-circuit if one fails.
I built a form validation system using Spring mvc.
This is my object User
public class User{
#NotEmpty
#NotBlank
#Size(min=3,max=20)
private String name;
.....
}
So far, if you don't fill the "name" field you get 3 messages:
may not be empty
may not be blank
size must be between 3 and 20
Is there a way, for example, to get the message 2 just if the message 1 is not sent?
I explain better: if a user doesn't fill a field I just want to print the message "may not be empty". Then, if the user fills a field with just spaces I want to print the message "may not be blank".
Then, if the field is filled (then is not empty) and doesn't contain just spaces (then is not blank) I want to print the size message.
Is there a way to handle this issue with annotation or I have to create my own validator class?
I looked into it on the internet but I didn't find a solution, probably because it's difficult to explain Google what to look for.
You can use group and #GroupSequence to achive order with your validator:
http://docs.jboss.org/hibernate/validator/4.1/reference/en-US/html/validator-usingvalidator.html#validator-usingvalidator-validationgroups
You can create your own annotation and use the meta constraint #ReportAsSingleViolation.
have a look at these websites
http://docs.jboss.org/hibernate/validator/4.0.1/reference/en/html/validator-customconstraints.html
http://docs.jboss.org/hibernate/validator/4.0.1/reference/en-US/html/validator-customconstraints.html
So your code could look
#ReportAsSingleViolation
#NotEmpty
#NotBlank
#Length(min = 3, max = 20)
public #interface CustomAnnotation {
public abstract String message() default "{customkey}";
public abstract Class<?>[] groups() default {};
public abstract Class<?>[] payload() default {};
}
Say I have validation on a field like this:
#NotEmpty
#Digits(integer = 3, fraction = 0)
private String code;
Using Spring MVC and Hibernate validation currently I get both messages if I leave the form field blank. Is there a way to only display the #NotEmpty message?
If you want to stick to the Bean Validation Specification you need to use a group sequence. Only a groups sequence guarantees an ordered constraint evaluation which stops on the first error. Something like:
#GroupSequence({ First.class, Second.class })
public class MyBean {
#NotEmpty(groups = First.class)
#Digits(integer = 3, fraction = 0, groups = Second.class)
private String code;
}
#NotNull(message = "{email.error}")
#Valid(message = "{email.error}")
private String email;
#NotNull(message = "{password.error}")
#Size(min = 8, max = 16, message = "{password.error}")
private String password;
Is there anyway to group the constraints that belong to a single attribute?
So if one constraint is not passed, this will throw the error in question. If multiple constraints are not valid, this would still throw only this one error.
Let me illustrate what type I am after:
#List(constraints = {
#NotNull,
#Valid
}, message = "{email.error}")
private String email;
#List(constraints = {
#NotNull,
#Size(min = 8, max = 16)
}, message = "{password.error}")
private String password;
Is something like this provided or what I have to create my own Validator for this?
Thanks in advance.
You are probably looking for the group respectively group sequence feature. Check the documentation for that. There are heaps of examples.
Regarding your example, #Valid on a String does not make sense. This annotation is for cascaded validation. Again check the documentation if you want to know more. Also an annotation like #List does not exist and wold not even compile.
I want to allow only positive integers for number fields including zero. How can I define this validation using JSR 303 ?
I tried
#Min(value=0 message = "msg1") - But it allows float values like 1.2.
#Digits(fraction = 0, integer = 10, message ="msg2") - It accepts negative values.
#Min(value=0, message = "msg1" )
#Digits(fraction = 0, integer = 10, message ="msg2") - It works fine but sometimes both the messages i.e. msg1 and msg2 are displayed.
Any suggestions?
Thanks!
Just use the annotation #Min in your bean:
#Min(value = 0L, message = "The value must be positive")
private Double value;
Looks like you are looking for natural numbers, I think you can use the regex pattern to get the desired output. Something like
#Pattern(regexp = "[\\s]*[0-9]*[1-9]+",message="msg")
If you use hibernate-validator then you may create a custom constraint which combines #Min and #Digits from the 3rd option by using #ConstraintComposition(AND). When you add #ReportAsSingleViolation, only a custom message will be shown.
You can use #Positive that works for the int and its wrappers too (ie Integer). This also will work for the BigInteger as well.
Its better to use range annotation like below for positive numbers
#Range(min = 0l, message = "Please select positive numbers Only")
For negative numbers
#Range(min = -9223372036854775808l, max = 0l, message = "Please select Negative numbers Only")
Another kind of solution and in my personal opinion cleaner and easier to read.
#Positive
#Digits(integer = 5, fraction = 0)
This is an example code from the answer https://stackoverflow.com/a/41675990/258544 above
#Documented
#Min(value=0, message = "add a min msg" )
#Digits(fraction = 0, integer = 10, message ="add a digit msg")
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {})
#ReportAsSingleViolation
public #interface NumberFormatValidator {
String message() default "invalid number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
It use Constraint composition http://docs.jboss.org/hibernate/validator/4.3/reference/en-US/html_single/#validator-customconstraints-compound and #ReportAsSingleViolation to avoid display both messages
Change the data time of your field from int to Integer and a message.properties file to set the message
Example: assuming your fieldName is myNumericField and belongs to a class called Test
Change the datatype:
change private int myNumericField;
to private Integer myNumericField;
Update getters and setters to use/return Integer
Create custom message.properties file.
This may appear to be more work but it's a more elegant and scalable solution rather than having the messages hardcoded in your annotations
Create a message.properties file under resources directory
Edit messages.properties and add the following line
typeMismatch.Test.myNumericField=Please use integers only
Remember to change Test and myNumericField for your class and field name
Edit your Application Context xml file and add the following tags before the <\beans> tag
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="resources/messages" />
</bean>
Using the following combination solved the issue for me:
#NotNull
#Range(min = 1)
#JsonProperty("end_range")
private Integer endRange;
Make sure to use Integer class instead of int for #NotNull to work.