Home > Mobile >  Custom class validator throws all errors even if only one field is not valid
Custom class validator throws all errors even if only one field is not valid

Time:10-09

In Java Spring boot i made class custome validator. And if even one field fail in Validator i get always both error messages

[ "WRONG_CAR_COLOR", "WRONG_YEAR_OF_PRODUCTION" ]

public class ElectricCarSpecValidator implements ConstraintValidator<ElectricCarSpec, CreateCarCommand> {

private static final Integer minYearOfProduction = 2000;
private static final Integer carColorRed = 10;
private static final Integer electricCarId = 5;

@Override
public boolean isValid(CreateCarCommand command, ConstraintValidatorContext context) {
    if (!command.getFuelTypeId().equals(electricCarId)) {
        return true;
    }
    return command.getYearOfProduction() >= minYearOfProduction && !command.getCarColorId().equals(carColorRed);
}

Validator:

    @ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
    List<String> errors = ex.getAllErrors().stream()
            .map(DefaultMessageSourceResolvable::getDefaultMessage)
            .collect(Collectors.toList());
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);
}

Adnotation:

@ElectricCarSpec.List({
    @ElectricCarSpec(field = "yearOfProduction", message = "WRONG_YEAR_OF_PRODUCTION"),
    @ElectricCarSpec(field = "carColorId", message = "WRONG_CAR_COLOR")

})

CodePudding user response:

Instead of implementing ConstraintValidator, you should implement org.springframework.validation.Validator. It contains a validate method in which you can validate multiple fields separately like

public void validate(Object target, Errors errors) {
  CreateCarCommand createCarCommand = (CreateCarCommand) target;
  if (!command.getFuelTypeId().equals(electricCarId)) {
     return;
  }
  if(command.getYearOfProduction() < minYearOfProduction) {
    errors.rejectValue("yearOfProduction", "WRONG_YEAR_OF_PRODUCTION");
  }
  if(!command.getCarColorId().equals(carColorRed)) {
       errors.rejectValue("carColorId", "WRONG_CAR_COLOR");
  }
}

In controller class, declare validator like

    @Autowired
    //@Qualifier("ElectricCarSpecValidator") //use if multiple implementations
    private Validator electricCarSpecValidator;

and in controller method,

electricCarSpecValidator.validate(createCarCommand, bindingResult);
if(bindingResult.hasErrors()) {
   //Errors found
   model.addAttribute("org.springframework.validation.BindingResult.createCarCommand", bindingResult);
}

CodePudding user response:

The problem is that whatever field you specify, you always do the very same validation - checking if both fields are valid.

return command.getYearOfProduction() >= minYearOfProduction 
     && !command.getCarColorId().equals(carColorRed);

In order to take your field property into account, you would need to override initialize(A constraintAnnotation). It's guaranteed by the docs to be called before isValid.

Initializes the validator in preparation for isValid(Object, ConstraintValidatorContext) calls. The constraint annotation for a given constraint declaration is passed. This method is guaranteed to be called before any use of this instance for validation.

Something like this:

public class TestConstraintValidator implements ConstraintValidator<ElectricCarSpec, CreateCarCommand> {

    private String field;

    @Override
    public void initialize(ElectricCarSpec constraintAnnotation) {
        this.field = constraintAnnotation.field();
    }

    @Override
    public boolean isValid(CreateCarCommand value, ConstraintValidatorContext context) {
        if (field.equals("yearOfProduction")) {
            //validate year of production
        }
        if (field.equals("carColorId")) {
            //validate color id
        }
        return true;
    }
}

This would require either many if conditions for every field you want to validate or writing a strategy pattern to pick correct validation strategy depending on field value. Personally i would avoid that and go for custom annotations on the field level - one annotation per field that must be validated.

  • Related