Annotation return default key value - java

when we use annotation
#NotNull and there is a constraint validation who happen
not null return automatically his message
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) #Retention(RUNTIME) #Repeatable(List.class) #Documented #Constraint(validatedBy = { }) public #interface NotNull {
String message() default "{jakarta.validation.constraints.NotNull.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
/** * Defines several {#link NotNull} annotations on the same element. * * #see jakarta.validation.constraints.NotNull */ #Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) #Retention(RUNTIME) #Documented #interface List {
NotNull[] value(); } }
it there a way to return his key so:
jakarta.validation.constraints.NotNull.message

Assuming you are asking for a way to get the message key after a constraint violation was received - you should be able to do that by working with that object. In particular, what you should look for - ConstraintViolation#getMessageTemplate(). This returns the non-interpolated error message for a constraint violation.
For example, having a class:
class Test {
#NotNull
String string;
public Test(String string) {
this.string = string;
}
}
and then trying to do validation of the instance of such class:
Validator validator = Validation.buildDefaultValidatorFactory().getValidator()
Set<ConstraintViolation<Test>> violations = validator.validate( new Test( null ) );
ConstraintViolation<Test> violation = violations.iterator().next();
assertEquals( "{jakarta.validation.constraints.NotNull.message}", violation.getMessageTemplate() );
If you are working with some frameworks and you catch an exception of ConstraintViolationException - look at ConstraintViolationException#getConstraintViolations(), which would give you that same collection of violations as in the example above.

Related

Is there any chance to validate all the values of the DTO Object is null in Request Body

#Controller......
#PutMapping(value = "/{id}")
public ResponseEntity<JsonNode> editStudent( #PathVariable #Positive(message = "Student id must be Positive Value") Long id, #Valid #ValidRequestBody(DTOClass = StudentDTO.class) #Validated(value = Update.class) #RequestBody(required = true) StudentDTO studentDTO, BindingResult bindingResult ) {.....
----------------------------------------------------------------------------
#Documented
#Retention(RUNTIME)
#Target({ TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.MODULE, ElementType.PACKAGE, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE })
#Constraint(validatedBy = RequestBodyValidator.class)
public #interface ValidRequestBody {
String message()
default "Required parameter is missing";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<?> DTOClass();
}
-----------------------------------------------------
public class RequestBodyValidator implements ConstraintValidator<ValidRequestBody, Map<String, Object>> {
Class<?> dtoClass = null;
#Override
public void initialize( ValidRequestBody constraintAnnotation ) {
this.dtoClass = constraintAnnotation.DTOClass();
}
#Override
public boolean isValid( Map<String, Object> objectNode, ConstraintValidatorContext context ) {
Collection<Object> objectValues = objectNode.values();
return !objectValues.stream().allMatch( null );
}
}
You could use #NotNull over all fields
https://www.baeldung.com/java-bean-validation-not-null-empty-blank
You can use #Valid or #NonNull annotation on all fields in your DTO. So, if a field is received as null or empty, exception would be thrown.

ConstraintValidation Not Working for a Rest Api Request

I created an API and added an custom-annotation to validate the Request body object, but this was never getting called. Below is the Object. Please go through the code and help me out where the code need to be corrected?
#NotNull, #Size is also not working
Request Body Object
#Getter
#AllArgsConstructor
#Sample
public class SaleRequest {
#NotNull
private Integer sale;
#NotNull
private Date dateTime;
#NotNull
#Size(min = 10, max = 10)
private String customerId;
}
Annotation
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {SalesRequestValidator.class})
#Documented
public #interface Sample {
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
String message() default "Invalid Sale Request";
}
Validator
public class SalesRequestValidator implements ConstraintValidator<Sample, SaleRequest> {
private String message;
#Override
public void initialize(Sample constraintAnnotation) {
this.message = constraintAnnotation.message();
}
#Override
public boolean isValid(SaleRequest sale, ConstraintValidatorContext context) {
System.out.println("Tested!");
return sale.getSale() > 0;
}
}
Api Implementation
public interface SalesApi {
#RequestMapping(
value = {"/sales"},
produces = {"application/json"},
consumes = {"application/json"},
method = {RequestMethod.POST}
)
ResponseEntity<Integer> submitSale(#RequestBody #Valid SaleRequest saleRequest);
}
Could not figure where I went wrong
Implementation looks ok.
Make sure you have set the #Valid annotation in your controller method where you expect to receive the request body of SaleRequest.
It should look something like this:
addNewSaleRequest(#RequestBody #Valid SaleRequest saleRequest)
Try to extend #Target({ElementType.TYPE}) with ElementType.PARAMETER as you want to validate a method parameter with it.
A #Validated annotation is needed on the related Controller as well.

javax.validation.UnexpectedTypeException using Spring ConstraintValidator for an enumeration

I have implemented a ConstraintValidator in order to valide a DTO that contains an enumeration. I followed this Spring documentation for that.
This is the custom annotation to be applied to the enum field:
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR,
ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = EnumValidator.class)
public #interface ValidEnum {
String message() default "{com.test.validation.constraints.ValidEnum}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends Enum<?>> target();
}
The EnumValidator looks like this:
public class EnumValidator implements ConstraintValidator<ValidEnum,String> {
private Set<String> enumValues;
#Override
public void initialize(ValidEnum targetEnum) {
Class<? extends Enum> enumSelected = targetEnum.targetClassType();
enumValues = (Set<String>) EnumSet.allOf(enumSelected).stream()
.map(e -> ((Enum<? extends Enum<?>>) e).name()).collect(Collectors
.toSet());
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return enumValues.contains(value);
}
}
This is the enum:
public enum Gender {
MALE,FEMALE;
}
This is the DTO to validate:
public final class UserDTO{
#ValidEnum(target = Gender.class)
private Gender gender;
#NotEmpty
#Max(100)
private String fullName;
}
And the controller that is validating the field:
#Controller
public class RegistrationController {
private static final String REGISTER_USER = "/register";
private final RegistrationService registrationService;
#PostMapping(value = REGISTER_USER)
#Consumes(APPLICATION_JSON)
#Produces(APPLICATION_JSON)
public UserRegistrationResponse register(#Valid UserDTO userRegistrationRequest) {
return registrationService.register(userRegistrationRequest);
}
}
It seems that Spring is not detecting the validator, because it throws this exception:
org.springframework.web.util.NestedServletException: Request processing failed;
nested exception is javax.validation.UnexpectedTypeException: HV000030:
No validator could be found for constraint 'com.test.ws.web.validation.ValidEnumType' validating type 'com.test.ws.domain.model.Gender'. Check configuration for 'gender'
I am using Spring boot 2.0.4 which include the required dependencies for validation.
Any idea why it fails?
You have three mistakes in your code.
#Max(100) this annotation shouldn't be applied to String field! only for numeric type. If you need to specify String size range restrictions you may use #Size(min = 2, max = 250) annotation.
You forgot RequestBody annotation in the controller method signature:
register(#Valid #RequestBody UserDTO userRegistrationRequest)
Enum validation consume String value from Rest API, not Enum itself. That's why you've got an error here.
I've created test project for you. Please check it here https://github.com/alex-petrov81/stackoverflow-answers/tree/master/enum-validator

How insert field name when using internationalization with javax validations?

I'm using javax validation annotations with Spring Boot and internationalization. So I have the following field:
#Size(min = 3, max = 3, message = "{javax.validation.constraints.Size.message}")
private String currencyCode;
The US resource bundle has:
javax.validation.constraints.Size.message = size must be between {min} and {max}
So when this field fails on validation, I see the message "size must be between 3 and 3".
But what I want is to have a message like:
javax.validation.constraints.Size.message = {fieldName}'s size must be between {min} and {max}
Which would result in the message "currencyCode's size must be between 3 and 3".
Is this possible? Do I need to override a bean to make it work? Is there a pre-defined property for the field name?
You can create handler for MethodArgumentNotValidException
#ExceptionHandler(MethodArgumentNotValidException.class)
public Object handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) { ...
inside them you can invoke
e.getBindingResult().getFieldErrors();
This is collection with all informations about invalid field error eg. field name, rejected value.
At next, use message like in your's example and use StrSubstitutor from org.apache.commons.lang3. Just create map of parameters
key --> fieldName , value --> currencyCode
Of course you have to write some code, but here is the solution :)
By default #Size annotation does not provide option to put label in the message, instead you can create your own implementation like below so that it can be used across your application:
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = MySizeValidator.class)
public #interface MySize {
String message() default "{javax.validation.constraints.Size.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int min() default 0;
int max() default Integer.MAX_VALUE;
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Documented
#interface List {
Size[] value();
}
public abstract String label();
}
then create your own validator like MySizeValidator to validate MySize annotation like below
public class MySizeValidator implements ConstraintValidator<MySize, String> {
private static final Log log = LoggerFactory.make();
private int min;
private int max;
#Override
public void initialize(MySize parameters) {
min = parameters.min();
max = parameters.max();
validateParameters();
}
#Override
public boolean isValid(String field, ConstraintValidatorContext constraintValidatorContext) {
if (field == null) {
return true;
}
int length = field.length();
return length >= min && length <= max;
}
private void validateParameters() {
if (min < 0) {
throw log.getMinCannotBeNegativeException();
}
if (max < 0) {
throw log.getMaxCannotBeNegativeException();
}
if (max < min) {
throw log.getLengthCannotBeNegativeException();
}
}
}
Create ValidationMessages.properties with below key value
javax.validation.constraints.mySize.message={label}'s size must be between {min} and {max}
then in your POJO, you can put this new annotation,
#MySize(min = 3, max = 3, message = "{javax.validation.constraints.mySize.message}", label = "Currency Code")
private String currencyCode;

Validating date with Spring Boot or Hibernate Validator

I've built a REST Service using Spring Boot. I'm also using Hibernate Validator to validate data. I have a REST endpoint like this:
#PostMapping(value = "${apiVersion.v_1}" + "/parameter-dates")
public ResponseEntity createParameterDate( #RequestBody ParameterDate parameterDate){
// Some code that use parameterDate
}
ParameterDate is defined in a class like this:
public class ParameterDate {
#NotNull(message = "Parameter Date Unadjusted can not be blank or null")
private Date parameterDateUnadjusted;
#NotNull(message = "Parameter Date Adjusted can not be blank or null")
private Date parameterDateAdjusted;
private Date parameterDateAdded;
private Date parameterDateChanged;
}
I would like to validate parameterDateUnadjusted and parameterDateAdjusted to make sure both of them are valid dates. I've tried with #DateTimeFormat(pattern = "yyyy-MM-dd") but it won't give me a validation error for not validate as long as they stick to yyyy-MM-dd. One example would be 2017-01-40 that it just interpret as 2017-02-09. I guess #DateTimeFormat is rather a formatter than a validator. I also tried using Hibernate Validator's #Pattern and rexexp like #Pattern(regexp="\\t(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\\d\\d\\t"). But this gives me the error
V000030: No validator could be found for constraint 'javax.validation.constraints.Pattern' validating type 'java.util.Date'. Check configuration for 'parameterDateAdjusted'
Any suggestion how I can validate these dates?
Here is an example to implement validator for Date object:
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = MyDateValidator.class)
#Documented
public #interface ValidDate {
String message() default "some message here";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
public class MyDateValidator implements ConstraintValidator<ValidDate, Date> {
public void initialize(ValidDate constraint) {
}
public boolean isValid(Date value, ConstraintValidatorContext context) {
// validate the value here.
}
}

Categories