SpringValidatorAdapter properly handles HV-5-style list constraint violations

Issue: SPR-15082
This commit is contained in:
Juergen Hoeller 2017-01-12 21:16:56 +01:00
parent b06423a5f8
commit d0e93284f3
2 changed files with 70 additions and 3 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -255,7 +255,7 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
protected Object getRejectedValue(String field, ConstraintViolation<Object> violation, BindingResult bindingResult) { protected Object getRejectedValue(String field, ConstraintViolation<Object> violation, BindingResult bindingResult) {
Object invalidValue = violation.getInvalidValue(); Object invalidValue = violation.getInvalidValue();
if (!"".equals(field) && (invalidValue == violation.getLeafBean() || if (!"".equals(field) && (invalidValue == violation.getLeafBean() ||
(field.contains(".") && !field.contains("[]")))) { (!field.contains("[]") && (field.contains("[") || field.contains("."))))) {
// Possibly a bean constraint with property path: retrieve the actual property value. // Possibly a bean constraint with property path: retrieve the actual property value.
// However, explicitly avoid this for "address[]" style paths that we can't handle. // However, explicitly avoid this for "address[]" style paths that we can't handle.
invalidValue = bindingResult.getRawFieldValue(field); invalidValue = bindingResult.getRawFieldValue(field);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -41,6 +41,7 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors; import org.springframework.validation.Errors;
@ -255,6 +256,23 @@ public class ValidatorFactoryTests {
assertNull(rejected); assertNull(rejected);
} }
@Test
public void testListValidation() throws Exception {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
ListContainer listContainer = new ListContainer();
listContainer.addString("A");
listContainer.addString("X");
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(listContainer, "listContainer");
errors.initConversion(new DefaultConversionService());
validator.validate(listContainer, errors);
FieldError fieldError = errors.getFieldError("list[1]");
assertEquals("X", errors.getFieldValue("list[1]"));
}
@NameAddressValid @NameAddressValid
public static class ValidPerson { public static class ValidPerson {
@ -424,4 +442,53 @@ public class ValidatorFactoryTests {
} }
} }
public static class ListContainer {
@NotXList
private List<String> list = new LinkedList<>();
public void addString(String value) {
list.add(value);
}
public List<String> getList() {
return list;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Constraint(validatedBy = NotXListValidator.class)
public @interface NotXList {
String message() default "Should not be X";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {};
}
public static class NotXListValidator implements ConstraintValidator<NotXList, List<String>> {
@Override
public void initialize(NotXList constraintAnnotation) {
}
@Override
public boolean isValid(List<String> list, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
boolean valid = true;
for (int i = 0; i < list.size(); i++) {
if ("X".equals(list.get(i))) {
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addBeanNode().inIterable().atIndex(i).addConstraintViolation();
valid = false;
}
}
return valid;
}
}
} }