My problem is that my ValidationMessages.properties is not working and Intellij Idea said
it was unused property.
My ValidationMessages.properties file:
com.atguigu.common.valid.ListValue.message=\u5FC5\u987B\u63D0\u4EA4\u6307\u5B9A\u7684\u503C
My ListValue file:
package com.atguigu.common.valid;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Documented
#Constraint(validatedBy = { ListValueConstraintValidator.class })
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
public #interface ListValue {
String message() default "{com.atguigu.common.valid.ListValue.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] vals() default { };
}
My ListValueConstraintValidator file:
package com.atguigu.common.valid;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<>();
//初始化方法
#Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
set.add(val);
}
}
//判断是否校验成功
/**
*
* #param value 需要校验的值
* #param context
* #return
*/
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
The validation-api dependency:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
The ScreenShot:
enter image description here
I try change the dependency version,but it doesn't work.
I expect this property can work normally.
Related
Is there any standard validator annotation I can use for a DataSize field?
Something like javax #Min and #Max, or Hibernate's #DurationMin and #DurationMax.
For class DataSize, there's no standard validator, so you should implement your own. But it's quite easy :)
Annotation interface:
import org.springframework.util.unit.DataUnit;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
#Documented
#Constraint(validatedBy = DataSizeMaxValidator.class)
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface DataSizeMax {
String message() default "must be less than or equal to {value} {unit}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
long value();
DataUnit unit();
}
Validator:
import org.springframework.stereotype.Component;
import org.springframework.util.unit.DataSize;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
#Component
public class DataSizeMaxValidator implements ConstraintValidator<DataSizeMax, DataSize> {
private DataSize dataSizeMax;
#Override
public void initialize(DataSizeMax dataSizeMax) {
this.dataSizeMax = DataSize.of(dataSizeMax.value(), dataSizeMax.unit());
}
#Override
public boolean isValid(DataSize value, ConstraintValidatorContext context) {
return dataSizeMax.compareTo(value) >= 0;
}
}
That's it. Then use this annotation as here:
import pizza.nikiforov.validators.DataSizeMax;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
public class Data {
#DataSizeMax(value = 15L, unit = DataUnit.MEGABYTES)
private DataSize dataSize;
// other fields
// constructors, getters and setters
}
Recently I added #Repeatable to my custom annotations. Once i added repeating annotaion to fields it was working fine , but fields with single annotoation is not working.
Example:
Following works:
#NotNull
#CustomValidator(type = Type.SIZE, max = 80)
#CustomValidator(type = Type.FIELD)
private String firstName;
But following does not works(Since it contains only one #CustomValidator):
#NotNull
#CustomValidator(type = Type.EMAIL)
private String email;
CustomValidator.java
import java.lang.annotation.Repeatable;
import javax.validation.Constraint;
import javax.validation.Payload;
#Constraint(validatedBy = CustomValidatorImpl.class)
#Repeatable(RepeatableCustomValidator.class)
public #interface CustomValidator {
String message() default "";
int min() default 1;
int max() default Integer.MAX_VALUE;
String format() default "dd.MM.yyyy";
String[] values() default {};
Type type();
boolean optional() default false;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
RepeatableCustomValidator.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface RepeatableCustomValidator {
CustomValidator[] value();
}
CustomValidatorImpl.java
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class CustomValidatorImpl implements ConstraintValidator<CustomValidator, String> {
...............
#Override
public void initialize(CustomValidator constraintAnnotation) {
..........
}
}
Dependencies:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
JAVA_VERSION="1.8.0_121"
Is there any thing that i am missing ?
As #Holger also pointed out,
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
Adding these two to your CustomValidator class fixes the problem.
By the way, Normal validations should also not take place if we do not mention these two policies.
Are you sure it was working without these? As I tried, and it was not working.
I would like to create a custom annotation in my Spring Boot application which always adds a prefix to my class level RequestMapping path.
My Controller:
import com.sagemcom.smartvillage.smartvision.common.MyApi;
import org.springframework.web.bind.annotation.GetMapping;
#MyApi("/users")
public class UserController {
#GetMapping("/stackoverflow")
public String get() {
return "Best users";
}
}
My custom annotation
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#RestController
#RequestMapping(path = "/api")
public #interface MyApi {
#AliasFor(annotation = RequestMapping.class)
String value();
}
GOAL: a mapping like this in the end: /api/users/stackoverflow
Notes:
server.servlet.context-path is not an option because I want to create
several of these
I'm using Spring Boot version 2.0.4
I was not able to find an elegant solution for the issue. However, this worked:
Slightly modified annotation, because altering behavior of value turned out to be more difficult.
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#RestController
#RequestMapping
public #interface MyApi {
#AliasFor(annotation = RequestMapping.class, attribute = "path")
String apiPath();
}
Bean Annotation Processor
import com.sagemcom.smartvillage.smartvision.common.MyApi;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
#Component
public class MyApiProcessor implements BeanPostProcessor {
private static final String ANNOTATIONS = "annotations";
private static final String ANNOTATION_DATA = "annotationData";
public Object postProcessBeforeInitialization(#NonNull final Object bean, String beanName) throws BeansException {
MyApi myApi = bean.getClass().getAnnotation(MyApi.class);
if (myApi != null) {
MyApi alteredMyApi = new MyApi() {
#Override
public Class<? extends Annotation> annotationType() {
return MyApi.class;
}
#Override
public String apiPath() {
return "/api" + myApi.apiPath();
}
};
alterAnnotationOn(bean.getClass(), MyApi.class, alteredMyApi);
}
return bean;
}
#Override
public Object postProcessAfterInitialization(#NonNull Object bean, String beanName) throws BeansException {
return bean;
}
#SuppressWarnings("unchecked")
private static void alterAnnotationOn(Class clazzToLookFor, Class<? extends Annotation> annotationToAlter, Annotation annotationValue) {
try {
// In JDK8 Class has a private method called annotationData().
// We first need to invoke it to obtain a reference to AnnotationData class which is a private class
Method method = Class.class.getDeclaredMethod(ANNOTATION_DATA, null);
method.setAccessible(true);
// Since AnnotationData is a private class we cannot create a direct reference to it. We will have to manage with just Object
Object annotationData = method.invoke(clazzToLookFor);
// We now look for the map called "annotations" within AnnotationData object.
Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS);
annotations.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) annotations.get(annotationData);
map.put(annotationToAlter, annotationValue);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Controller:
import com.sagemcom.smartvillage.smartvision.common.MyApi;
import org.springframework.web.bind.annotation.GetMapping;
#MyApi(apiPath = "/users")
public class UserController {
#GetMapping("/stackoverflow")
public String get() {
return "Best users";
}
}
I am trying to implement a custom password match validation in spring boot. But I am getting an error as follows:-
PasswordMatch contains Constraint annotation, but does not contain a message parameter
I am following this link https://www.baeldung.com/registration-with-spring-mvc-and-spring-security for custom validation. The problem is I am getting error as this.
javax.validation.ConstraintDefinitionException: HV000074: com.bikram.booking.validation.PasswordMatch contains Constraint annotation, but does not contain a message parameter.
at org.hibernate.validator.internal.metadata.core.ConstraintHelper.assertMessageParameterExists(ConstraintHelper.java:915)
I have searched solutions on web but couldn't find the decent soultion.
My Modal is
package com.bikram.booking.dto;
import com.bikram.booking.validation.PasswordMatch;
import com.bikram.booking.validation.ValidEmail;
import javax.validation.constraints.*;
#PasswordMatch
public class UserDto {
#NotNull
#Size(min = 6, message = "Password should be more than 6 characters")
#NotEmpty(message = "Please provide a password")
private String password;
#NotNull
#Size(min = 6, message = "Password should be more than 6 characters")
private String confirmPassword;
}
My Interface is
package com.bikram.booking.validation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Target({ TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = PasswordMatchValidator.class)
#Documented
public #interface PasswordMatch {
String messages() default "Sorry, passwords does not match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And Implementation is
package com.bikram.booking.validation;
import com.bikram.booking.dto.UserDto;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, Object> {
#Override
public void initialize(PasswordMatch constraintAnnotation) {
}
#Override
public boolean isValid(Object obj, ConstraintValidatorContext constraintValidatorContext) {
UserDto userDto = (UserDto) obj;
return true;
}
}
Any hints will be higly appreciable.
Rename messages() to message() in PasswordMatch :
public #interface PasswordMatch {
String message() default "Sorry, passwords does not match";
....
}
The structure that I have is something like below:
Class A{
String str;
int i;
List<B> bs;
C c;
#NotNull
List<D> ds;
}
Class B{
#NotNull
List<E> es;
}
Class C{
List<String> s;
}
Class E{
#NotNull
List<String> s;
}
For the list variables that are annotated with #NotNull I need to throw validation error if any of them variables has one or more null objects. While for the other list variables I just need to remove the nulls;
What would be the best way to achieve this?
If you are using validation 2.0+ you can put annotation inside: List<#NotNull String> s;
You should define custom annotation for validating.
so define custom annotation like bellow.
#Target({ElementType.FIELD, ElementType.PARAMETER,ElementType.ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = ValidateListValidator.class)
#Documented
public #interface ValidateList {
}
and implement ValidateListValidator like this:
public class ValidateListValidator implements ConstraintValidator<ValidateList, List<Object>> {
private ValidateList validateList;
#Override
public void initialize(ValidateList validateList) {
this.validateList = validateList;
}
#Override
public boolean isValid( List<Object> list, ConstraintValidatorContext constraintValidatorContext) {
return list.stream().noneMatch(Objects::isNull);
}
}
and for test it
#Test
public void test() {
boolean valid = validator.isValid(Arrays.asList("test","this",null),context);
assertThat(valid, is(false));
}
This is the final code that I wrote, just a few tweaks to the code that Hadi posted. I hope it helps:
Annotation:
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
#Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = ListValidator.class)
#Documented
public #interface ValidList {
String message() default "Null values are not allowed in array fields.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validator Class:
import java.util.List;
import java.util.Objects;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class ListValidator implements ConstraintValidator<ValidList, List<? extends Object>> {
#Override
public boolean isValid(List<? extends Object> list, ConstraintValidatorContext context) {
return list.stream().noneMatch(Objects::isNull);
}
#Override
public void initialize(ValidList constraintAnnotation) {}
}