diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java index d9f93d6e823..a78b51704d4 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java @@ -76,7 +76,10 @@ public class ValidationBindHandler extends AbstractBindHandler { public Object onFailure(ConfigurationPropertyName name, Bindable target, BindContext context, Exception error) throws Exception { Object result = super.onFailure(name, target, context, error); - validate(name, target, context, null); + if (result != null) { + this.exceptions.clear(); + } + validate(name, target, context, result); return result; } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java index fe4307c4440..7c712023541 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java @@ -167,7 +167,7 @@ public class ValidationBindHandlerTests { @Test public void bindShouldValidateIfOtherHandlersInChainIgnoreError() { - TestHandler testHandler = new TestHandler(); + TestHandler testHandler = new TestHandler(null); this.handler = new ValidationBindHandler(testHandler, this.validator); this.sources.add(new MockConfigurationPropertySource("foo", "hello")); ExampleValidatedBean bean = new ExampleValidatedBean(); @@ -177,6 +177,21 @@ public class ValidationBindHandlerTests { .withCauseInstanceOf(BindValidationException.class); } + @Test + public void bindShouldValidateIfOtherHandlersInChainReplaceErrorWithResult() { + TestHandler testHandler = new TestHandler(new ExampleValidatedBeanSubclass()); + this.handler = new ValidationBindHandler(testHandler, this.validator); + this.sources.add(new MockConfigurationPropertySource("foo", "hello")); + this.sources.add(new MockConfigurationPropertySource("foo.age", "bad")); + this.sources.add(new MockConfigurationPropertySource("foo.years", "99")); + ExampleValidatedBean bean = new ExampleValidatedBean(); + assertThatExceptionOfType(BindException.class) + .isThrownBy(() -> this.binder.bind("foo", + Bindable.of(ExampleValidatedBean.class).withExistingValue(bean), this.handler)) + .withCauseInstanceOf(BindValidationException.class) + .satisfies((ex) -> assertThat(ex.getCause()).hasMessageContaining("years")); + } + private BindValidationException bindAndExpectValidationError(Runnable action) { try { action.run(); @@ -219,6 +234,25 @@ public class ValidationBindHandlerTests { } + public static class ExampleValidatedBeanSubclass extends ExampleValidatedBean { + + @Min(100) + private int years; + + public ExampleValidatedBeanSubclass() { + setAge(20); + } + + public int getYears() { + return this.years; + } + + public void setYears(int years) { + this.years = years; + } + + } + @Validated public static class ExampleValidatedWithNestedBean { @@ -282,10 +316,16 @@ public class ValidationBindHandlerTests { static class TestHandler extends AbstractBindHandler { + private Object result; + + TestHandler(Object result) { + this.result = result; + } + @Override public Object onFailure(ConfigurationPropertyName name, Bindable target, BindContext context, Exception error) throws Exception { - return null; + return this.result; } }