Fix return value validation

Fix argument in call to applyReturnValueValidation()
method in MethodValidationInterceptor.java. Method
argument was passed instead of the return value of the
method that was being validated.

See gh-33105
This commit is contained in:
vatsal 2024-06-26 20:00:01 -07:00 committed by rstoyanchev
parent 100da83913
commit 976b4f3533
2 changed files with 81 additions and 12 deletions

View File

@ -174,7 +174,7 @@ public class MethodValidationInterceptor implements MethodInterceptor {
Object returnValue = invocation.proceed();
if (this.adaptViolations) {
this.validationAdapter.applyReturnValueValidation(target, method, null, arguments, groups);
this.validationAdapter.applyReturnValueValidation(target, method, null, returnValue, groups);
}
else {
violations = this.validationAdapter.invokeValidatorForReturnValue(target, method, returnValue, groups);

View File

@ -20,8 +20,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import jakarta.validation.ValidationException;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.groups.Default;
@ -45,7 +47,9 @@ import org.springframework.scheduling.annotation.AsyncAnnotationAdvisor;
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.function.SingletonSupplier;
import org.springframework.validation.annotation.Validated;
import org.springframework.validation.method.MethodValidationException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@ -66,7 +70,19 @@ class MethodValidationProxyTests {
ProxyFactory factory = new ProxyFactory(bean);
factory.addAdvice(new MethodValidationInterceptor());
factory.addAdvisor(new AsyncAnnotationAdvisor());
doTestProxyValidation((MyValidInterface<String>) factory.getProxy());
doTestProxyValidation((MyValidInterface<String>) factory.getProxy(), ConstraintViolationException.class);
}
@Test
@SuppressWarnings("unchecked")
public void testMethodValidationInterceptorWithAdaptConstraintViolations() {
MyValidBean bean = new MyValidBean();
ProxyFactory factory = new ProxyFactory(bean);
try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) {
factory.addAdvice(new MethodValidationInterceptor(SingletonSupplier.of(validatorFactory.getValidator()), true));
factory.addAdvisor(new AsyncAnnotationAdvisor());
doTestProxyValidation((MyValidInterface<String>) factory.getProxy(), MethodValidationException.class);
}
}
@Test
@ -79,7 +95,26 @@ class MethodValidationProxyTests {
context.registerSingleton("aapp", AsyncAnnotationBeanPostProcessor.class, pvs);
context.registerSingleton("bean", MyValidBean.class);
context.refresh();
doTestProxyValidation(context.getBean("bean", MyValidInterface.class));
doTestProxyValidation(context.getBean("bean", MyValidInterface.class), ConstraintViolationException.class);
context.close();
}
@Test
@SuppressWarnings("unchecked")
public void testMethodValidationPostProcessorWithAdaptConstraintViolations() {
StaticApplicationContext context = new StaticApplicationContext();
context.registerBean(MethodValidationPostProcessor.class, () -> {
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
postProcessor.setAdaptConstraintViolations(true);
return postProcessor;
});
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("beforeExistingAdvisors", false);
context.registerSingleton("aapp", AsyncAnnotationBeanPostProcessor.class, pvs);
context.registerSingleton("bean", MyValidBean.class);
context.refresh();
doTestProxyValidation(context.getBean("bean", MyValidInterface.class), MethodValidationException.class);
context.close();
}
@ -90,21 +125,38 @@ class MethodValidationProxyTests {
context.registerBean(MyValidInterface.class, () ->
ProxyFactory.getProxy(MyValidInterface.class, new MyValidClientInterfaceMethodInterceptor()));
context.refresh();
doTestProxyValidation(context.getBean(MyValidInterface.class));
doTestProxyValidation(context.getBean(MyValidInterface.class), ConstraintViolationException.class);
context.close();
}
@Test
@SuppressWarnings("unchecked")
public void testMethodValidationPostProcessorForInterfaceOnlyProxyWithAdaptConstraintViolations() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean(MethodValidationPostProcessor.class, () -> {
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
postProcessor.setAdaptConstraintViolations(true);
return postProcessor;
});
context.registerBean(MyValidInterface.class, () ->
ProxyFactory.getProxy(MyValidInterface.class, new MyValidClientInterfaceMethodInterceptor()));
context.refresh();
doTestProxyValidation(context.getBean(MyValidInterface.class), MethodValidationException.class);
context.close();
}
@SuppressWarnings("DataFlowIssue")
private void doTestProxyValidation(MyValidInterface<String> proxy) {
private void doTestProxyValidation(MyValidInterface<String> proxy, Class<? extends Exception> expectedExceptionClass) {
assertThat(proxy.myValidMethod("value", 5)).isNotNull();
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> proxy.myValidMethod("value", 15));
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> proxy.myValidMethod(null, 5));
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> proxy.myValidMethod("value", 0));
assertThatExceptionOfType(expectedExceptionClass).isThrownBy(() -> proxy.myValidMethod("value", 15));
assertThatExceptionOfType(expectedExceptionClass).isThrownBy(() -> proxy.myValidMethod(null, 5));
assertThatExceptionOfType(expectedExceptionClass).isThrownBy(() -> proxy.myValidMethod("value", 0));
proxy.myValidAsyncMethod("value", 5);
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> proxy.myValidAsyncMethod("value", 15));
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> proxy.myValidAsyncMethod(null, 5));
assertThatExceptionOfType(expectedExceptionClass).isThrownBy(() -> proxy.myValidAsyncMethod("value", 15));
assertThatExceptionOfType(expectedExceptionClass).isThrownBy(() -> proxy.myValidAsyncMethod(null, 5));
assertThat(proxy.myGenericMethod("myValue")).isEqualTo("myValue");
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> proxy.myGenericMethod(null));
assertThatExceptionOfType(expectedExceptionClass).isThrownBy(() -> proxy.myGenericMethod(null));
}
@Test
@ -122,6 +174,11 @@ class MethodValidationProxyTests {
doTestLazyValidatorForMethodValidation(LazyMethodValidationConfigWithValidatorProvider.class);
}
@Test
void testLazyValidatorForMethodValidationWithAdaptConstraintViolations() {
doTestLazyValidatorForMethodValidation(LazyMethodValidationConfigWithAdaptConstraintViolations.class);
}
private void doTestLazyValidatorForMethodValidation(Class<?> configClass) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(configClass, CustomValidatorBean.class, MyValidBean.class, MyValidFactoryBean.class);
@ -278,4 +335,16 @@ class MethodValidationProxyTests {
}
}
@Configuration
public static class LazyMethodValidationConfigWithAdaptConstraintViolations {
@Bean
public static MethodValidationPostProcessor methodValidationPostProcessor(ObjectProvider<Validator> validator) {
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
postProcessor.setValidatorProvider(validator);
postProcessor.setAdaptConstraintViolations(true);
return postProcessor;
}
}
}