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.