Skip to content

Commit 1773f6e

Browse files
committed
Apply validation only when annotations detected
Closes gh-445
1 parent 76f97da commit 1773f6e

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataFetcherHandlerMethod.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public DataFetcherHandlerMethod(HandlerMethod handlerMethod,
7171
super(handlerMethod, executor);
7272
Assert.isTrue(!resolvers.getResolvers().isEmpty(), "No argument resolvers");
7373
this.resolvers = resolvers;
74-
this.validator = validator;
74+
this.validator = (validator != null && validator.requiresValidation(handlerMethod) ? validator : null);
7575
this.subscription = subscription;
7676
}
7777

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/HandlerMethodValidationHelper.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@
1919
import java.lang.annotation.Annotation;
2020
import java.util.Set;
2121

22+
import javax.validation.Constraint;
2223
import javax.validation.ConstraintViolation;
2324
import javax.validation.ConstraintViolationException;
25+
import javax.validation.Valid;
2426
import javax.validation.Validator;
27+
import javax.validation.metadata.BeanDescriptor;
2528

2629
import org.springframework.context.ApplicationContext;
30+
import org.springframework.core.MethodParameter;
2731
import org.springframework.core.annotation.AnnotationUtils;
32+
import org.springframework.core.annotation.MergedAnnotations;
2833
import org.springframework.graphql.data.method.HandlerMethod;
2934
import org.springframework.lang.Nullable;
3035
import org.springframework.util.Assert;
@@ -97,6 +102,34 @@ private static <A extends Annotation> A findAnnotation(HandlerMethod method, Cla
97102
return annotation;
98103
}
99104

105+
/**
106+
* Whether the given method requires standard bean validation. This is the
107+
* case if the method or one of its parameters are annotated with
108+
* {@link Valid} or {@link Validated}, or if any method parameter is declared
109+
* with a {@link Constraint constraint}, or the method parameter type is
110+
* itself {@link BeanDescriptor#isBeanConstrained() constrained}.
111+
* @param method the handler method to check
112+
* @return {@code true} if validation is required, {@code false} otherwise
113+
*/
114+
public boolean requiresValidation(HandlerMethod method) {
115+
if (findAnnotation(method, Validated.class) != null || findAnnotation(method, Valid.class) != null) {
116+
return true;
117+
}
118+
for (MethodParameter parameter : method.getMethodParameters()) {
119+
for (Annotation annotation : parameter.getParameterAnnotations()) {
120+
MergedAnnotations merged = MergedAnnotations.from(annotation);
121+
if (merged.isPresent(Valid.class) || merged.isPresent(Constraint.class) || merged.isPresent(Validated.class)) {
122+
return true;
123+
}
124+
}
125+
Class<?> paramType = parameter.nestedIfOptional().getNestedParameterType();
126+
if (this.validator.getConstraintsForClass(paramType).isBeanConstrained()) {
127+
return true;
128+
}
129+
}
130+
return false;
131+
}
132+
100133

101134
/**
102135
* Factory method for {@link HandlerMethodValidationHelper} if a

spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/HandlerMethodValidationHelperTests.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
import java.lang.annotation.RetentionPolicy;
2121
import java.lang.reflect.Method;
2222
import java.util.Arrays;
23+
import java.util.Optional;
2324

2425
import javax.validation.ConstraintViolation;
2526
import javax.validation.ConstraintViolationException;
27+
import javax.validation.Valid;
2628
import javax.validation.Validation;
2729
import javax.validation.constraints.Max;
2830
import javax.validation.constraints.NotNull;
@@ -36,6 +38,7 @@
3638
import org.springframework.graphql.data.method.HandlerMethod;
3739
import org.springframework.validation.annotation.Validated;
3840

41+
import static org.assertj.core.api.Assertions.assertThat;
3942
import static org.assertj.core.api.Assertions.assertThatNoException;
4043
import static org.assertj.core.api.Assertions.assertThatThrownBy;
4144

@@ -80,6 +83,27 @@ void shouldRaiseValidationErrorForAnnotatedParamsWithGroups() {
8083
.anyMatch(violation -> violation.getPropertyPath().toString().equals("myValidMethodWithGroupOnType.arg0"));
8184
}
8285

86+
@Test
87+
void shouldRecognizeMethodsThatRequireValidation() {
88+
HandlerMethod method = findHandlerMethod(RequiresValidationBean.class, "processConstrainedValue");
89+
assertThat(validator.requiresValidation(method)).isTrue();
90+
91+
method = findHandlerMethod(RequiresValidationBean.class, "processValidInput");
92+
assertThat(validator.requiresValidation(method)).isTrue();
93+
94+
method = findHandlerMethod(RequiresValidationBean.class, "processValidatedInput");
95+
assertThat(validator.requiresValidation(method)).isTrue();
96+
97+
method = findHandlerMethod(RequiresValidationBean.class, "processInputWithConstrainedValue");
98+
assertThat(validator.requiresValidation(method)).isTrue();
99+
100+
method = findHandlerMethod(RequiresValidationBean.class, "processOptionalInputWithConstrainedValue");
101+
assertThat(validator.requiresValidation(method)).isTrue();
102+
103+
method = findHandlerMethod(RequiresValidationBean.class, "processValue");
104+
assertThat(validator.requiresValidation(method)).isFalse();
105+
}
106+
83107

84108
private HandlerMethod findHandlerMethod(Class<?> handlerType, String methodName) {
85109
Object handler = BeanUtils.instantiateClass(handlerType);
@@ -147,4 +171,47 @@ public Object myValidMethodWithGroupOnType(@NotNull(groups = {SecondGroup.class}
147171

148172
}
149173

174+
175+
@SuppressWarnings("unused")
176+
private static class RequiresValidationBean {
177+
178+
public void processConstrainedValue(@Max(99) int i) {
179+
}
180+
181+
public void processValidInput(@Valid MyInput input) {
182+
}
183+
184+
public void processValidatedInput(@Validated MyInput input) {
185+
}
186+
187+
public void processInputWithConstrainedValue(MyConstrainedInput input) {
188+
}
189+
190+
public void processOptionalInputWithConstrainedValue(Optional<MyConstrainedInput> input) {
191+
}
192+
193+
public void processValue(int i) {
194+
}
195+
196+
}
197+
198+
199+
private static class MyInput {
200+
}
201+
202+
private static class MyConstrainedInput {
203+
204+
@Max(99)
205+
private int i;
206+
207+
public int getI() {
208+
return this.i;
209+
}
210+
211+
public void setI(int i) {
212+
this.i = i;
213+
}
214+
215+
}
216+
150217
}

0 commit comments

Comments
 (0)