Skip to content

Commit e4c0ff5

Browse files
committed
Merge pull request #26787 from dreis2211
* pr/26787: Avoid exceptions when evaluating validation hints Closes gh-26787
2 parents d275a4e + e7cbe23 commit e4c0ff5

File tree

5 files changed

+85
-50
lines changed

5 files changed

+85
-50
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.validation.annotation;
18+
19+
import java.lang.annotation.Annotation;
20+
21+
import org.springframework.core.annotation.AnnotationUtils;
22+
import org.springframework.lang.Nullable;
23+
24+
/**
25+
* Utility class for handling validation annotations.
26+
* Mainly for internal use within the framework.
27+
*
28+
* @author Christoph Dreis
29+
* @since 5.3.7
30+
*/
31+
public abstract class ValidationAnnotationUtils {
32+
33+
/**
34+
* Determine any validation hints by the given annotation.
35+
* <p>This implementation checks for {@code @javax.validation.Valid},
36+
* Spring's {@link org.springframework.validation.annotation.Validated},
37+
* and custom annotations whose name starts with "Valid".
38+
* @param ann the annotation (potentially a validation annotation)
39+
* @return the validation hints to apply (possibly an empty array),
40+
* or {@code null} if this annotation does not trigger any validation
41+
* @since 5.3.7
42+
*/
43+
@Nullable
44+
public static Object[] determineValidationHints(Annotation ann) {
45+
Class<? extends Annotation> annotationType = ann.annotationType();
46+
String annotationName = annotationType.getName();
47+
if ("javax.validation.Valid".equals(annotationName)) {
48+
return new Object[0];
49+
}
50+
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
51+
if (validatedAnn != null) {
52+
Object hints = validatedAnn.value();
53+
return convertValidationHints(hints);
54+
}
55+
if (annotationType.getSimpleName().startsWith("Valid")) {
56+
Object hints = AnnotationUtils.getValue(ann);
57+
return convertValidationHints(hints);
58+
}
59+
return null;
60+
}
61+
62+
private static Object[] convertValidationHints(@Nullable Object hints) {
63+
if (hints == null) {
64+
return new Object[0];
65+
}
66+
return (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints});
67+
}
68+
69+
}

spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,7 +37,6 @@
3737
import org.springframework.beans.BeanUtils;
3838
import org.springframework.beans.TypeMismatchException;
3939
import org.springframework.core.MethodParameter;
40-
import org.springframework.core.annotation.AnnotationUtils;
4140
import org.springframework.lang.Nullable;
4241
import org.springframework.util.Assert;
4342
import org.springframework.util.StringUtils;
@@ -46,7 +45,7 @@
4645
import org.springframework.validation.Errors;
4746
import org.springframework.validation.SmartValidator;
4847
import org.springframework.validation.Validator;
49-
import org.springframework.validation.annotation.Validated;
48+
import org.springframework.validation.annotation.ValidationAnnotationUtils;
5049
import org.springframework.web.bind.WebDataBinder;
5150
import org.springframework.web.bind.annotation.ModelAttribute;
5251
import org.springframework.web.bind.support.WebDataBinderFactory;
@@ -362,7 +361,7 @@ else if (StringUtils.startsWithIgnoreCase(request.getHeader("Content-Type"), "mu
362361
*/
363362
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
364363
for (Annotation ann : parameter.getParameterAnnotations()) {
365-
Object[] validationHints = determineValidationHints(ann);
364+
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
366365
if (validationHints != null) {
367366
binder.validate(validationHints);
368367
break;
@@ -388,7 +387,7 @@ protected void validateValueIfApplicable(WebDataBinder binder, MethodParameter p
388387
Class<?> targetType, String fieldName, @Nullable Object value) {
389388

390389
for (Annotation ann : parameter.getParameterAnnotations()) {
391-
Object[] validationHints = determineValidationHints(ann);
390+
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
392391
if (validationHints != null) {
393392
for (Validator validator : binder.getValidators()) {
394393
if (validator instanceof SmartValidator) {
@@ -406,26 +405,6 @@ protected void validateValueIfApplicable(WebDataBinder binder, MethodParameter p
406405
}
407406
}
408407

409-
/**
410-
* Determine any validation triggered by the given annotation.
411-
* @param ann the annotation (potentially a validation annotation)
412-
* @return the validation hints to apply (possibly an empty array),
413-
* or {@code null} if this annotation does not trigger any validation
414-
* @since 5.1
415-
*/
416-
@Nullable
417-
private Object[] determineValidationHints(Annotation ann) {
418-
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
419-
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
420-
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
421-
if (hints == null) {
422-
return new Object[0];
423-
}
424-
return (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
425-
}
426-
return null;
427-
}
428-
429408
/**
430409
* Whether to raise a fatal bind exception on validation errors.
431410
* <p>The default implementation delegates to {@link #isBindExceptionRequired(MethodParameter)}.

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.springframework.core.ReactiveAdapter;
3232
import org.springframework.core.ReactiveAdapterRegistry;
3333
import org.springframework.core.ResolvableType;
34-
import org.springframework.core.annotation.AnnotationUtils;
3534
import org.springframework.core.codec.DecodingException;
3635
import org.springframework.core.codec.Hints;
3736
import org.springframework.core.io.buffer.DataBuffer;
@@ -45,7 +44,7 @@
4544
import org.springframework.lang.Nullable;
4645
import org.springframework.util.Assert;
4746
import org.springframework.validation.Validator;
48-
import org.springframework.validation.annotation.Validated;
47+
import org.springframework.validation.annotation.ValidationAnnotationUtils;
4948
import org.springframework.web.bind.support.WebExchangeBindException;
5049
import org.springframework.web.bind.support.WebExchangeDataBinder;
5150
import org.springframework.web.reactive.BindingContext;
@@ -240,10 +239,9 @@ private ServerWebInputException handleMissingBody(MethodParameter parameter) {
240239
private Object[] extractValidationHints(MethodParameter parameter) {
241240
Annotation[] annotations = parameter.getParameterAnnotations();
242241
for (Annotation ann : annotations) {
243-
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
244-
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
245-
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
246-
return (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
242+
Object[] hints = ValidationAnnotationUtils.determineValidationHints(ann);
243+
if (hints != null) {
244+
return hints;
247245
}
248246
}
249247
return null;

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,14 +30,13 @@
3030
import org.springframework.core.ReactiveAdapter;
3131
import org.springframework.core.ReactiveAdapterRegistry;
3232
import org.springframework.core.ResolvableType;
33-
import org.springframework.core.annotation.AnnotationUtils;
3433
import org.springframework.lang.Nullable;
3534
import org.springframework.ui.Model;
3635
import org.springframework.util.Assert;
3736
import org.springframework.util.ClassUtils;
3837
import org.springframework.validation.BindingResult;
3938
import org.springframework.validation.Errors;
40-
import org.springframework.validation.annotation.Validated;
39+
import org.springframework.validation.annotation.ValidationAnnotationUtils;
4140
import org.springframework.web.bind.annotation.ModelAttribute;
4241
import org.springframework.web.bind.support.WebExchangeBindException;
4342
import org.springframework.web.bind.support.WebExchangeDataBinder;
@@ -270,16 +269,9 @@ private boolean hasErrorsArgument(MethodParameter parameter) {
270269

271270
private void validateIfApplicable(WebExchangeDataBinder binder, MethodParameter parameter) {
272271
for (Annotation ann : parameter.getParameterAnnotations()) {
273-
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
274-
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
275-
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
276-
if (hints != null) {
277-
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
278-
binder.validate(validationHints);
279-
}
280-
else {
281-
binder.validate();
282-
}
272+
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
273+
if (validationHints != null) {
274+
binder.validate(validationHints);
283275
}
284276
}
285277
}

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636

3737
import org.springframework.core.MethodParameter;
3838
import org.springframework.core.ResolvableType;
39-
import org.springframework.core.annotation.AnnotationUtils;
4039
import org.springframework.core.log.LogFormatUtils;
4140
import org.springframework.http.HttpHeaders;
4241
import org.springframework.http.HttpInputMessage;
@@ -52,7 +51,7 @@
5251
import org.springframework.util.Assert;
5352
import org.springframework.util.StreamUtils;
5453
import org.springframework.validation.Errors;
55-
import org.springframework.validation.annotation.Validated;
54+
import org.springframework.validation.annotation.ValidationAnnotationUtils;
5655
import org.springframework.web.HttpMediaTypeNotSupportedException;
5756
import org.springframework.web.bind.WebDataBinder;
5857
import org.springframework.web.context.request.NativeWebRequest;
@@ -241,10 +240,8 @@ protected ServletServerHttpRequest createInputMessage(NativeWebRequest webReques
241240
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
242241
Annotation[] annotations = parameter.getParameterAnnotations();
243242
for (Annotation ann : annotations) {
244-
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
245-
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
246-
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
247-
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
243+
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
244+
if (validationHints != null) {
248245
binder.validate(validationHints);
249246
break;
250247
}

0 commit comments

Comments
 (0)