I am trying to validate Double in the request of my dropwizard application. Below is the code for same
import javax.validation.constraints.NotNull;
#PATH("/path")
public class MyResource {
#GET
public Response addNumber( #QueryParam("first") #NotNull Double first, #QueryParam("second") #NotNull Double second) {
return Response.ok("added").build();
}
}
I get the below error when querying the resource localhost/path?first=46.76&second=67.56
{
"errors": [
"query param first may not be null",
"query param second may not be null"
]
}
If we remove the #NotNull from the resource the resource responds successfully.
Why does #NotNull say it should be not null although the values are present?
I had the same requirement. So implemented custom validator.
#Retention(RUNTIME)
#Target({ElementType.FIELD})
#Constraint(validatedBy = NotZeroDoubleValidatorImpl.class)
public #interface NotZeroDoubleValidator {
String message() default "{Double value is required}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Field to validate against 0.
*/
double field() default 0;
}
And
public class NotZeroDoubleValidatorImpl implements ConstraintValidator<NotZeroDoubleValidator, Double> {
#Override
public void initialize(final NotZeroDoubleValidator notZeroDoubleValidator) {
}
#Override
public boolean isValid(final Double value, final ConstraintValidatorContext context) {
return Double.compare(value, 0) != 0;
}
}
Related
I am a java beginner and I need help with testing this:
what I am trying to do: I need at least one of the fields with value to accept it.
How should test my implementation?
My classes:
#Constraint(validatedBy = BankOrBarPresenceValidador.class)
#Target({ TYPE, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Documented
public #interface VerifyBankOrBarPresence {
String message() default "{constraints.VerifyBankOrBarPresence.message}";
Class <?> [] groups() default {};
Class <? extends Payload> [] payload() default {};
String barCodeInvoice();
String bankDestinationNumber();
}
Implementation:
public class BankOrBarPresenceValidador implements ConstraintValidator<VerifyBankOrBarPresence, Object> {
private String barCodeInvoice;
private String bankDestinationNumber;
#Override
public void initialize(VerifyBankOrBarPresence constraintAnnotation) {
barCodeInvoice = constraintAnnotation.barCodeInvoice();
bankDestinationNumber = constraintAnnotation.bankDestinationNumber();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
try{
boolean barcodeIsPresent = barCodeInvoice.length() > 0;
boolean bankDestinationNumberIsPresent = bankDestinationNumber.length() > 0;
return barcodeIsPresent || bankDestinationNumberIsPresent;
}
catch (final Exception e) {
throw new BusinessException("Failed validating if bar code or bank destination number are present", e);
}
}
}
MY DTO:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
#VerifyBankOrBarPresence(barCodeInvoice = "barCodeInvoice", bankDestinationNumber="bankDestinationNumber", message = "Bar Code Invoice or bank Destination Number code not be null")
public class VoucherDTO implements Serializable {
private String barCodeInvoice;
private String bankDestinationNumber;
}
In my spring boot application I want to validate enum by custom value:
I have my DTO like following :
#Data
public class PayOrderDTO {
#NotNull
#EnumValidator(enumClass = TransactionMethod.class)
private TransactionMethod method;
}
And my enum validator annotation defined like bellow:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD})
#Constraint(validatedBy = EnumValidatorImpl.class)
public #interface EnumValidator {
String message() default "is invalid";
/**
* #return Specify group
*/
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* #return Specifies the enumeration type. The parameter value must be a value in this enumeration type
*/
Class<? extends EnumBase> enumClass();
/**
* #return Can it be null
*/
boolean nullable() default false;
/**
* #return Values to exclude
*/
int[] exclusion() default {};
}
This is the implementation of my enum validator annotation
public class EnumValidatorImpl implements ConstraintValidator<EnumValidator, EnumBase> {
private boolean nullable;
private Set<String> values;
#Override
public void initialize(EnumValidator constraintAnnotation) {
this.nullable = constraintAnnotation.nullable();
Class<? extends EnumBase> enumClass = constraintAnnotation.enumClass();
int[] exclusion = constraintAnnotation.exclusion();
values = new HashSet<>();
EnumBase[] enumConstants = enumClass.getEnumConstants();
for (EnumBase iEnum : enumConstants) {
values.add(iEnum.getValue());
}
if (exclusion.length > 0)
for (int i : exclusion) {
values.remove(i);
}
}
#Override
public boolean isValid(EnumBase param, ConstraintValidatorContext constraintValidatorContext) {
if (nullable && param == null) {
return true;
}
else if(param == null)
return false;
return values.contains(param.getValue());
}
}
this is my enum:
public enum TransactionMethod implements EnumBase {
CREDIT_CARD("creditcard"),
DEBIT_CARD("debitcard");
public String label;
TransactionMethod(String label) {
this.label = label;
}
#Override
public String getValue() {
return this.label;
}
#JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static TransactionMethod fromString(String value) {
return TransactionMethod.valueOf(value);
// return Arrays.stream(TransactionMethod.values())
// .filter(el -> el.getValue().equals(value))
// .findFirst()
// .orElseThrow(() -> {
// throw new IllegalArgumentException("Not valid method");
// });
}
}
when I'm sending my http request to that rest controller :
#RequiredArgsConstructor
#RestController
#RequestMapping("/orders")
#Validated
public class PaymentRestController {
public ResponseEntity<?> createPayment(
#Valid #RequestBody PayOrderDTO payOrderDTO
) {
return ResponseEntity.ok("Worked");
}
}
request example:
POST /orders/ HTTP/1.1
Host: store.test
Content-Type: application/json
Content-Length: 152
{
"method":"creditcard",
}
I'm expecting to get invalidation exception or error message defined in my enum validator, instead I get an exception in the console that contains :
JSON parse error: Cannot construct instance of `x.TransactionMethod`, problem: No enum constant x.TransactionMethod.creditcard
But if I sent this request :
POST /orders/ HTTP/1.1
Host: store.test
Content-Type: application/json
Content-Length: 152
{
"method":"CREDIT_CARD",
}
the application works normal
I want to validate enum using label instead of constant value of the enum, if it doesn't exists, a validation error will be thrown like :
HTTP 422 : field 'method' is not valid, expected values are ['creditcard','debitcard']
I tried some solutions as well like the convertor
public class TransactionMethodStringEnumConverter implements Converter<String, TransactionMethod> {
#Override
public TransactionMethod convert(String source) {
Optional<TransactionMethod> first = Arrays.stream(TransactionMethod.values()).filter(e -> e.label.equals(source)).findFirst();
return first.orElseThrow(() -> {
throw new IllegalArgumentException();
});
}
}
but seems like I does nothing.
I would really appreciate is someone has a good solution for this, Thank you 🙏
To deserialize an enum by label value you can use #JsonValue annotation on a getter:
public enum TransactionMethod implements EnumBase {
CREDIT_CARD("creditcard"),
DEBIT_CARD("debitcard");
public String label;
TransactionMethod(String label) {
this.label = label;
}
#Override
#JsonValue
public String getValue() {
return this.label;
}
}
How To Serialize and Deserialize Enums with Jackson
Also, pay attention to the facts:
You have values.remove(i) in the initialize() method of EnumValidatorImpl, although elements of Set are not indexed and Set<String> values has generic type String.
In your EnumValidator you can set nullable=true using boolean nullable(), but in PayOrderDTO you still check the method field for null using #NotNull, which can lead to an undesirable result.
EDIT:
You can define locale-specific messages with MessageSource, look at this article.
In your message.properties: invalid.transaction.method=is not valid, expected values are ['creditcard','debitcard']
Then add message to annotation:
#EnumValidator(enumClass = TransactionMethod.class, message = "{invalid.transaction.method}")
By failed validation catchMethodArgumentNotValidException in #ExceptionHandler method in #RestController or #RestControllerAdvice and format message as you want, e.g.:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public void handleException(MethodArgumentNotValidException e) {
String errorMessage = e.getBindingResult().getFieldErrors().stream()
.map(fieldError -> fieldError.getField() + " " + fieldError.getDefaultMessage())
.collect(Collectors.joining(";"));
LOG.error(e.getMessage()); // method is not valid, expected values are ['creditcard','debitcard']
}
I created an annotation in order to validate the value of a String field (like an enum) :
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = GenericStringSetValidator.class)
public #interface ValidStringValue {
String message() default "Must be a valid value. Found: ${validatedValue}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] values() default {};
}
And the GenericStringSetValidator used by the annotation :
public class GenericStringSetValidator implements ConstraintValidator<ValidStringValue, String> {
private Set<String> validValues;
#Override
public void initialize(ValidStringValue constraint) {
validValues = Arrays.stream(constraint.values())
.collect(toSet());
}
#Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if (value == null) return false;
return validValues.contains(value);
}
}
I already use this in the following class whithout any trouble :
public class SampleClass{
private String id;
#ValidStringValue(values = {"IN_PROGRESS", "COMPLETED", "DELETED"})
private String status;
}
But I also want to use it in an other class with List<String> in order to validate each items of the List. Is it possible to configure my ValidStringValue interface to make it usable on a String and also a List<String> ?
public class SampleClassWithList{
private String id;
#ValidStringValue(values = {"A", "B", "C"})
private List<String> sampleList;
}
Thanks a lot for your help :)
I am using hibernate validator for validating DTO object. Here facing one chalange not able check data is available or not in Enum
I have used below enum
public enum DeliveryMethodEnum {
IMMEDIATE(1),
SCHEDULED(2);
private Integer deliveryMethod;
private DeliveryMethodEnum(Integer deliveryMethod) {
this.deliveryMethod = deliveryMethod;
}
public Integer getDeliveryMethod() {
return deliveryMethod;
}
}
In DTO use custom annotation as below
#EnumValidator(message = "1008", enumClass =
DeliveryMethodEnum.class) private Integer deliveryMethod;
Request is below
{
"deliveryMethod": 8
}
It should show invalid request but its not valided from enum.
I have below custome annotation code
#Documented
#Constraint(validatedBy = EnumValidatorImpl.class)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#NotNull(message = "Value cannot be null")
#ReportAsSingleViolation
public #interface EnumValidator {
Class<? extends Enum<?>> enumClass();
String message() default "Value is not valid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class EnumValidatorImpl implements ConstraintValidator<EnumValidator, Integer> {
List<String> valueList = null;
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return !valueList.contains(String.valueOf(value));
}
#Override
public void initialize(EnumValidator constraintAnnotation) {
valueList = new ArrayList<>();
Class<? extends Enum<?>> enumClass = constraintAnnotation.enumClass();
#SuppressWarnings("rawtypes")
Enum[] enumValArr = enumClass.getEnumConstants();
for (#SuppressWarnings("rawtypes")
Enum enumVal : enumValArr) {
valueList.add(enumVal.toString().toUpperCase());
}
}
}
This is because there is error in your EnumValidatorImpl class.
Following line
!valueList.contains(String.valueOf(value));
Actual value of valueList: [IMMEDIATE, SCHEDULED]
and value: 8 from body payload {"deliveryMethod": 8}
So validation will fail always.
Also !valueList.contains this will be valid for non-enum value,
if you need to validate for only enum values then remove ! symbol.
Solutions
1. Accept as String from request as
{
"deliveryMethod": "IMMEDIATE"
}
or
{
"deliveryMethod": "SCHEDULED"
}
Hence need to updateDTO, Now value is one of the enum/not and gets validated
Create valueList as [1, 2] and validate.
Here is updated EnumValidatorImpl.java
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class EnumValidatorImpl implements ConstraintValidator<EnumValidator, Integer> {
List<Integer> valueList = null;
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return valueList.contains(value);
}
#Override
public void initialize(EnumValidator constraintAnnotation) {
valueList = new ArrayList<>();
Class<? extends Enum<?>> enumClass = constraintAnnotation.enumClass();
Enum[] enumValArr = enumClass.getEnumConstants();
valueList = Arrays.stream(enumValArr)
.map(anEnum -> ((DeliveryMethodEnum) anEnum).deliveryMethod)
.collect(Collectors.toList()); // This will make problem, since it will only be used for DeliveryMethodEnum enum type and not for any other enum, if it is not the problem then this can be done,else I could not think of any other way
}
}
When writing custom constraints is it possible to have multiple annotations validated by one validator implementation. For example I have several annotations which stipulate different #size annotations but I want them all to point at the same validator class which does some global checking i.e. all must be match a certain regex. As far as I can see the implementation takes in one Annotation Type.
One annotation
#Target( { METHOD, FIELD, ANNOTATION_TYPE, TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {UCNValidator.class})
#Documented
#Size(min = 9, max = 9, message = "{exactlength}")
public #interface UCN {
String message() default "{invalidFormat}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String fieldName() default "ucn";
}
The validator
public class UCNValidator implements ConstraintValidator<UCN, String>
{
private String pattern = "[a-zA-Z].*";
private String fieldName;
#Override
public void initialize( UCN constraintAnnotation )
{
this.fieldName = constraintAnnotation.fieldName();
}
#Override
public boolean isValid( String value, ConstraintValidatorContext constraintValidatorContext )
{
if ( value != null )
{
if ( !value.matches(pattern) )
{
//do some stuff
return false;
}
}
return true;
}
There doesn't seem to be a way to access other values from an object when validating one of its properties. The solution I use is to put the annotation on the class, then the validator will get the entire object in to validate against, and you can access just the info you need to perform the validation.
Here's one I wrote to compare two different properties of an object against each other:
#Target(TYPE)
#Retention(RUNTIME)
#Constraint(validatedBy = LessThanValidator.class)
#Documented
public #interface LessThan {
String message() default "{com.bullethq.constraints.LessThan}";
String bigValueProperty();
String littleValueProperty();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Then the validator class is:
public class LessThanValidator implements ConstraintValidator<LessThan, Object> {
private LessThan constraint;
public void initialize(LessThan constraintAnnotation) {
constraint = constraintAnnotation;
}
public boolean isValid(Object object, ConstraintValidatorContext cvc) {
Object bigValue = getValue(object, constraint.bigValueProperty());
Object littleValue = getValue(object, constraint.littleValueProperty());
// If one of the values is null, then we do not perform validation.
if (bigValue == null || littleValue == null) {
return true;
}
if (bigValue instanceof Comparable && littleValue instanceof Comparable) {
boolean valid = ((Comparable<Object>) bigValue).compareTo(littleValue) > 0;
if (!valid) {
// If the values are not valid, then build a custom violations which has the correct path in it.
cvc.buildConstraintViolationWithTemplate(cvc.getDefaultConstraintMessageTemplate())
.addNode(constraint.littleValueProperty())
.addConstraintViolation().disableDefaultConstraintViolation();
}
return valid;
}
throw new IllegalArgumentException("Properties " + constraint.bigValueProperty() + " and " + constraint.littleValueProperty() + " both need to be comparable in " + object.getClass());
}
}
The getValue() method is just a static method using reflection to get the value from the object.