How to use ValidationMessages properties file with parameters in Spring Boot? - java

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.

Related

Can I create custom validation annotation that inherits from javax.constrains annotations

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;

What is the method signature means? String value() default "";

I came across this method signature in Spring Component interface.
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Indexed
public #interface Component
{
String value() default "";
}
What is the method signature String value() default ""; means?
How and When should we define such syntax for our coding purposes?
This is no method signature. it means that you can pass a String as parameter to the Component annotation, like this:
#Component(value = "value")
If you don't specify a value your self, the default value "" will be used.
If it had been like this:
String value(); // without the default
value would have been a mandatory parameter. Trying to use Component like this:
#Component()
would lead to an Exception, since you didn't provide a value.
EDIT: when to use.
If you don't know much about this syntax, or annotations in general, you shouldn't use them. About everything that can be done using annotations, especially custom made ones, can also be done without annotations.
Let's say you want to create an annotation to validate the value of a field.
I'll be using the example of Belgian postal codes. They all are 4 digits, and are between 1000 and 9999.
#Target( {ElementType.FIELD})
#Retention( RetentionPolicy.RUNTIME)
#Constraint( validatedBy = ValidatePostalCodeImpl.class)
public #interface ValidatePostalCode{
String message() default "You have entered an invalid postal code";
Class<?>[] groups() default {}; // needed for the validation
Class<? extends Payload>[] payload() default{}; // needed for the validation
int maxValue() default 9999; // so, by default, this will validate based
int minValue() default 1000; // on these values, but you will be able to
// override these
}
/* Validation implementation */
public class ValidatePostalCodeImpl implements ConstraintValidator<ValidatePostalCode, Integer> {
int upperValue;
int lowerValue;
#Override
public void initialize(ValidatePostalCode validate) {
this.upperValue = validate.maxValue(); // here you call them as if they indeed were regular methods
this.lowerValue = validate.minValue();
}
#Override
public boolean isValid(Integer integer, ConstraintValidatorContext context) {
return integer >= lowerValue && integer <= upperValue;
}
}
/* Usage */
#Entity
#Table(name = "addresses")
public class Addresses {
// home address -> In Belgium, so has to be between the default values:
#ValidatePostalCode
Integer belgianPostalCode;
// vacation home in another country, let's say the PC's there are between
// 12000 and 50000
#ValidatePostalCode(minValue = 12000, maxValue = 50000)
Integer foreignPostalCode;
}
Sure, this is a very limited example, but it should get you an idea.
The #interface keyword is used to define an annotation. This annotation has a property called value, which you could specify explicitly:
#Component(value = "myValue")
Or in the shorthand form:
#Component("myValue")
If you don't specify the value, it will default to "", as defined by the default keyword.

Validate UUID Restful service

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

How to order Bean Validation

I have create a validation annotation to validate the content of a string. The definition of my annotation looks like this:
#NotNull
#Size(min = 2, max = 128, groups = First.class)
#Pattern(regexp = "^(?!WP_([0-9])+$)[A-Z_][A-Z0-9_-]+", groups = Second.class)
#Target({FIELD, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = {})
#GroupSequence(value={First.class, Second.class})
public #interface ValidIntentName {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
My problem is that i cannot order the #Size and #Pattern execution. I've tried a couple of things but no luck so far.
Basically I need #Size to be executed before #Pattern
First and Second interfaces are defined. There are no errors during compilation is just that during run-time #Patter is executed before #Size.
Any idea what I'm doing wrong?
I'm running Java 8.
Thanks

Combining custom constraint validator annotation

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,

Categories

Resources