Support method validation for Lists in WebMvc and WebFlux

Closes gh-31120
This commit is contained in:
rstoyanchev
2023-09-04 13:21:59 +01:00
parent 6597727c86
commit b068742ec8
5 changed files with 120 additions and 16 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.web.method;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.StringJoiner;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -383,19 +384,25 @@ public class HandlerMethod extends AnnotatedMethod {
*/
private static class MethodValidationInitializer {
private static final Predicate<MergedAnnotation<? extends Annotation>> INPUT_PREDICATE =
private static final Predicate<MergedAnnotation<? extends Annotation>> CONSTRAINT_PREDICATE =
MergedAnnotationPredicates.typeIn("jakarta.validation.Constraint");
private static final Predicate<MergedAnnotation<? extends Annotation>> OUTPUT_PREDICATE =
MergedAnnotationPredicates.typeIn("jakarta.validation.Valid", "jakarta.validation.Constraint");
private static final Predicate<MergedAnnotation<? extends Annotation>> VALID_PREDICATE =
MergedAnnotationPredicates.typeIn("jakarta.validation.Valid");
public static boolean checkArguments(Class<?> beanType, MethodParameter[] parameters) {
if (AnnotationUtils.findAnnotation(beanType, Validated.class) == null) {
for (MethodParameter parameter : parameters) {
MergedAnnotations merged = MergedAnnotations.from(parameter.getParameterAnnotations());
if (merged.stream().anyMatch(INPUT_PREDICATE)) {
if (merged.stream().anyMatch(CONSTRAINT_PREDICATE)) {
return true;
}
else {
Class<?> type = parameter.getParameterType();
if (merged.stream().anyMatch(VALID_PREDICATE) && List.class.isAssignableFrom(type)) {
return true;
}
}
}
}
return false;
@@ -404,7 +411,7 @@ public class HandlerMethod extends AnnotatedMethod {
public static boolean checkReturnValue(Class<?> beanType, Method method) {
if (AnnotationUtils.findAnnotation(beanType, Validated.class) == null) {
MergedAnnotations merged = MergedAnnotations.from(method, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
return merged.stream().anyMatch(OUTPUT_PREDICATE);
return merged.stream().anyMatch(CONSTRAINT_PREDICATE.or(VALID_PREDICATE));
}
return false;
}

View File

@@ -38,14 +38,14 @@ public class HandlerMethodTests {
@Test
void shouldValidateArgsWithConstraintsDirectlyOnClass() {
Object target = new MyClass();
testShouldValidateArguments(target, List.of("addIntValue", "addPersonAndIntValue"), true);
testShouldValidateArguments(target, List.of("addIntValue", "addPersonAndIntValue", "addPersons"), true);
testShouldValidateArguments(target, List.of("addPerson", "getPerson", "getIntValue", "addPersonNotValidated"), false);
}
@Test
void shouldValidateArgsWithConstraintsOnInterface() {
Object target = new MyInterfaceImpl();
testShouldValidateArguments(target, List.of("addIntValue", "addPersonAndIntValue"), true);
testShouldValidateArguments(target, List.of("addIntValue", "addPersonAndIntValue", "addPersons"), true);
testShouldValidateArguments(target, List.of("addPerson", "addPersonNotValidated", "getPerson", "getIntValue"), false);
}
@@ -110,6 +110,9 @@ public class HandlerMethodTests {
public void addPersonAndIntValue(@Valid Person person, @Max(10) int value) {
}
public void addPersons(@Valid List<Person> persons) {
}
public void addPersonNotValidated(Person person) {
}
@@ -134,6 +137,8 @@ public class HandlerMethodTests {
void addPersonAndIntValue(@Valid Person person, @Max(10) int value);
void addPersons(@Valid List<Person> persons);
void addPersonNotValidated(Person person);
@Valid
@@ -159,6 +164,10 @@ public class HandlerMethodTests {
public void addPersonAndIntValue(Person person, int value) {
}
@Override
public void addPersons(List<Person> persons) {
}
@Override
public void addPersonNotValidated(Person person) {
}