-
Notifications
You must be signed in to change notification settings - Fork 38.6k
Description
Pavel Horal opened SPR-9332 and commented
SpringValidatorAdapter is converting JSR-303 ConstraintViolations to Spring's FieldErrors. This conversion contains a flaw, when the violation occurs on FIELD level constraint, which is validated as bean (e.g. subform).
Consider following example:
public class Address {
private String street;
private String number;
// Getters and Setters
}
public class UserForm {
@NotEmpty
private String name;
@AddressValid(/* some parameters */)
private Address contactAddress;
@AddressValid(/* some parameters */)
private Address billingAddress;
// Getters and Setters
}
public class AddressValidator implements ConstraintValidator<AddressValid, Address> {
// initialize ...
public boolean isValid(Address address, ConstraintValidationContext context) {
// check null, disableDefaultConstraintViolation ...
if (street == null || street.length() == 0) {
context.buildConstraintViolationWithTemplate("{validation.noStreet}")
.addNode(street).addConstraintViolation();
return false;
);
return true;
}
}
When submitting empty street the validator above will produce ConstraintValidation with Address instance as the invalidValue and UserForm instance as the leafBean. Now consider current SpringValidatorAdapter code:
protected void processConstraintViolations(Set<ConstraintViolation<Object>> violations, Errors errors) {
for (ConstraintViolation<Object> violation : violations) {
// ... irrelevant part of code
if (errors instanceof BindingResult) {
BindingResult bindingResult = (BindingResult) errors;
String nestedField = bindingResult.getNestedPath() + field;
if ("".equals(nestedField)) {
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode);
bindingResult.addError(new ObjectError(
errors.getObjectName(), errorCodes, errorArgs, violation.getMessage()));
}
else {
Object invalidValue = violation.getInvalidValue();
// ... THIS IS WHAT IS WRONG - the rejected value is the whole bean Address!!!
if (!"".equals(field) && invalidValue == violation.getLeafBean()) {
// bean constraint with property path: retrieve the actual property value
invalidValue = bindingResult.getRawFieldValue(field);
}
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
bindingResult.addError(new FieldError(
errors.getObjectName(), nestedField, invalidValue, false,
errorCodes, errorArgs, violation.getMessage()));
}
}
// ... irrelevant part of code
}
}
The impact of this behavior is critical - Spring's FORM tags are using rejected values as default values in forms. This means, that the user instead of the rejected value will see result of toString() method of the parent bean.
Correct behavior would be to check if the field contains path separator '*.*' instead of just checking invalidValue == violation.getLeafBean().
Affects: 3.1.1, 3.2 M1
Attachments:
- InnerBeanValidationTest.java (3.99 kB)
Issue Links:
- GenericConversionService.convert() throws IllegalArgumentException after updating to Spring 3.2.1 [SPR-10243] #14876 GenericConversionService.convert() throws IllegalArgumentException after updating to Spring 3.2.1
- Spring validation crashes with Hibernate Validation 5 style list constraint violations [SPR-15082] #19648 Spring validation crashes with Hibernate Validation 5 style list constraint violations
- NumberFormatException caused by property paths from JSR-303 based validation with no index into a collection [SPR-16177] #20725 NumberFormatException caused by property paths from JSR-303 based validation with no index into a collection
Referenced from: commits 8e75eee
1 votes, 2 watchers