How to make javax validation only display one error message - java

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;
}

Related

Is there annotation in java or micronaut that validates if given string can be deserialized to float or double?

I need to call a PUT or POST request with a payload
{
name:"dummy",
marks:"90.50"
}
And I have the following entity:
#Entity(name="Student")
public class Student {
private String name;
#Min(value = 0, message = "Marks should be positive.")
#Digits(integer = 2, fraction = 2, message = "value must be positive and 2 digits whole number")
private Float marks;
}
If I call API with marks as negative it gives me an appropriate error saying "Marks should be positive." because of the #Max annotation.
Similar to it, how can I show an appropriate message if I call API with invalid marks like "90.abc" or "abcd".
PS: I already have validation on the frontend, but need validation in the backend as well.
I´d say your marks property is already a Float. So how should it be possible to pass a String to this property? Isn´t it possible to directly design the API to have a number type for the JSON value type of marks?
If you really need the validation if a String is a number, it can be done with a #Pattern, e.g. #Pattern(regexp = "^\\d+(?:\\.\\d+)?\$").

Bean validation specification prioritizing

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.

Bean Validation range constraint

I need to implement range constraints on Entity data fields:
#Entity
#Table(name = "T_PAYMENT")
public class Payment extends AbstractEntity {
//....
//Something like that
#Range(minValue = 80, maxValue = 85)
private Long paymentType;
}
I already created validating service, but have to implement many of these cases.
I need the app to throw exception if the inserted number is out of range.
You need Hibernate Validator (see documentation)
Hibernate Validator
The Bean Validation reference implementation.
Application layer agnostic validation Hibernate Validator allows to
express and validate application constraints. The default metadata
source are annotations, with the ability to override and extend
through the use of XML. It is not tied to a specific application tier
or programming model and is available for both server and client
application programming. But a simple example says more than 1000
words:
public class Car {
#NotNull
private String manufacturer;
#NotNull
#Size(min = 2, max = 14)
private String licensePlate;
#Min(2)
private int seatCount;
// ...
}
With hibernate-validator dependency you can define range check
#Min(value = 80)
#Max(value = 85)
private Long paymentType;
In pom.xml add below dependency
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>{hibernate.version}</version>
</dependency>
For integer and long you can use #Min(value = 80)
#Max(value = 85)
For BigDecimal #DecimalMin(value = "80.99999")
#DecimalMax(value = "86.9999")
I believe this is the specific annotation you are looking for:
https://docs.jboss.org/hibernate/validator/4.1/api/org/hibernate/validator/constraints/Range.html
Example:
#Range(min = 1, max =12)
private String expiryMonth;
This is also a more beneficial annotation because it will work on Strings or Number variants without using two annotations like is the case with #Max/#Min. #Size is not compatible with Integers.

Group for Constraints in Bean Validation

#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.

Validate positive integers

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.

Categories

Resources