Align validation metadata handling in PayloadMethodArgumentResolver
Reuses ValidationAnnotationUtils which is slightly optimized for the detection of Spring's Validated annotation now, also to the benefit of common web scenarios. Closes gh-21852
This commit is contained in:
parent
1e75041b00
commit
c7269feeaa
|
@ -26,37 +26,46 @@ import org.springframework.lang.Nullable;
|
||||||
* Mainly for internal use within the framework.
|
* Mainly for internal use within the framework.
|
||||||
*
|
*
|
||||||
* @author Christoph Dreis
|
* @author Christoph Dreis
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 5.3.7
|
* @since 5.3.7
|
||||||
*/
|
*/
|
||||||
public abstract class ValidationAnnotationUtils {
|
public abstract class ValidationAnnotationUtils {
|
||||||
|
|
||||||
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine any validation hints by the given annotation.
|
* Determine any validation hints by the given annotation.
|
||||||
* <p>This implementation checks for {@code @jakarta.validation.Valid},
|
* <p>This implementation checks for Spring's
|
||||||
* Spring's {@link org.springframework.validation.annotation.Validated},
|
* {@link org.springframework.validation.annotation.Validated},
|
||||||
* and custom annotations whose name starts with "Valid".
|
* {@code @jakarta.validation.Valid}, and custom annotations whose
|
||||||
|
* name starts with "Valid" which may optionally declare validation
|
||||||
|
* hints through the "value" attribute.
|
||||||
* @param ann the annotation (potentially a validation annotation)
|
* @param ann the annotation (potentially a validation annotation)
|
||||||
* @return the validation hints to apply (possibly an empty array),
|
* @return the validation hints to apply (possibly an empty array),
|
||||||
* or {@code null} if this annotation does not trigger any validation
|
* or {@code null} if this annotation does not trigger any validation
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object[] determineValidationHints(Annotation ann) {
|
public static Object[] determineValidationHints(Annotation ann) {
|
||||||
|
// Direct presence of @Validated ?
|
||||||
|
if (ann instanceof Validated validated) {
|
||||||
|
return validated.value();
|
||||||
|
}
|
||||||
|
// Direct presence of @Valid ?
|
||||||
Class<? extends Annotation> annotationType = ann.annotationType();
|
Class<? extends Annotation> annotationType = ann.annotationType();
|
||||||
String annotationName = annotationType.getName();
|
if ("jakarta.validation.Valid".equals(annotationType.getName())) {
|
||||||
if ("jakarta.validation.Valid".equals(annotationName)) {
|
|
||||||
return EMPTY_OBJECT_ARRAY;
|
return EMPTY_OBJECT_ARRAY;
|
||||||
}
|
}
|
||||||
|
// Meta presence of @Validated ?
|
||||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||||
if (validatedAnn != null) {
|
if (validatedAnn != null) {
|
||||||
Object hints = validatedAnn.value();
|
return validatedAnn.value();
|
||||||
return convertValidationHints(hints);
|
|
||||||
}
|
}
|
||||||
|
// Custom validation annotation ?
|
||||||
if (annotationType.getSimpleName().startsWith("Valid")) {
|
if (annotationType.getSimpleName().startsWith("Valid")) {
|
||||||
Object hints = AnnotationUtils.getValue(ann);
|
return convertValidationHints(AnnotationUtils.getValue(ann));
|
||||||
return convertValidationHints(hints);
|
|
||||||
}
|
}
|
||||||
|
// No validation triggered
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1309,8 +1309,8 @@ public abstract class AnnotationUtils {
|
||||||
*/
|
*/
|
||||||
public static boolean isSynthesizedAnnotation(@Nullable Annotation annotation) {
|
public static boolean isSynthesizedAnnotation(@Nullable Annotation annotation) {
|
||||||
try {
|
try {
|
||||||
return ((annotation != null) && Proxy.isProxyClass(annotation.getClass()) &&
|
return (annotation != null && Proxy.isProxyClass(annotation.getClass()) &&
|
||||||
(Proxy.getInvocationHandler(annotation) instanceof SynthesizedMergedAnnotationInvocationHandler));
|
Proxy.getInvocationHandler(annotation) instanceof SynthesizedMergedAnnotationInvocationHandler);
|
||||||
}
|
}
|
||||||
catch (SecurityException ex) {
|
catch (SecurityException ex) {
|
||||||
// Security settings disallow reflective access to the InvocationHandler:
|
// Security settings disallow reflective access to the InvocationHandler:
|
||||||
|
|
|
@ -33,7 +33,6 @@ import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ReactiveAdapter;
|
import org.springframework.core.ReactiveAdapter;
|
||||||
import org.springframework.core.ReactiveAdapterRegistry;
|
import org.springframework.core.ReactiveAdapterRegistry;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
import org.springframework.core.codec.Decoder;
|
import org.springframework.core.codec.Decoder;
|
||||||
import org.springframework.core.codec.DecodingException;
|
import org.springframework.core.codec.DecodingException;
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
@ -54,17 +53,17 @@ import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.BeanPropertyBindingResult;
|
import org.springframework.validation.BeanPropertyBindingResult;
|
||||||
import org.springframework.validation.SmartValidator;
|
import org.springframework.validation.SmartValidator;
|
||||||
import org.springframework.validation.Validator;
|
import org.springframework.validation.Validator;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.ValidationAnnotationUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A resolver to extract and decode the payload of a message using a
|
* A resolver to extract and decode the payload of a message using a
|
||||||
* {@link Decoder}, where the payload is expected to be a {@link Publisher} of
|
* {@link Decoder}, where the payload is expected to be a {@link Publisher}
|
||||||
* {@link DataBuffer DataBuffer}.
|
* of {@link DataBuffer DataBuffer}.
|
||||||
*
|
*
|
||||||
* <p>Validation is applied if the method argument is annotated with
|
* <p>Validation is applied if the method argument is annotated with
|
||||||
* {@code @jakarta.validation.Valid} or
|
* {@link org.springframework.validation.annotation.Validated} or
|
||||||
* {@link org.springframework.validation.annotation.Validated}. Validation
|
* {@code @jakarta.validation.Valid}. Validation failure results in an
|
||||||
* failure results in an {@link MethodArgumentNotValidException}.
|
* {@link MethodArgumentNotValidException}.
|
||||||
*
|
*
|
||||||
* <p>This resolver should be ordered last if {@link #useDefaultResolution} is
|
* <p>This resolver should be ordered last if {@link #useDefaultResolution} is
|
||||||
* set to {@code true} since in that case it supports all types and does not
|
* set to {@code true} since in that case it supports all types and does not
|
||||||
|
@ -286,10 +285,8 @@ public class PayloadMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (Annotation ann : parameter.getParameterAnnotations()) {
|
for (Annotation ann : parameter.getParameterAnnotations()) {
|
||||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
|
||||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
if (validationHints != null) {
|
||||||
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
|
|
||||||
Object[] validationHints = (hints instanceof Object[] objectHints ? objectHints : new Object[] {hints});
|
|
||||||
String name = Conventions.getVariableNameForParameter(parameter);
|
String name = Conventions.getVariableNameForParameter(parameter);
|
||||||
return target -> {
|
return target -> {
|
||||||
BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(target, name);
|
BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(target, name);
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.lang.annotation.Annotation;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
import org.springframework.messaging.converter.MessageConversionException;
|
import org.springframework.messaging.converter.MessageConversionException;
|
||||||
|
@ -38,12 +37,16 @@ import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.validation.ObjectError;
|
import org.springframework.validation.ObjectError;
|
||||||
import org.springframework.validation.SmartValidator;
|
import org.springframework.validation.SmartValidator;
|
||||||
import org.springframework.validation.Validator;
|
import org.springframework.validation.Validator;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.ValidationAnnotationUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A resolver to extract and convert the payload of a message using a
|
* A resolver to extract and convert the payload of a message using a
|
||||||
* {@link MessageConverter}. It also validates the payload using a
|
* {@link MessageConverter}.
|
||||||
* {@link Validator} if the argument is annotated with a Validation annotation.
|
*
|
||||||
|
* <p>Validation is applied if the method argument is annotated with
|
||||||
|
* {@link org.springframework.validation.annotation.Validated} or
|
||||||
|
* {@code @jakarta.validation.Valid}. Validation failure results in an
|
||||||
|
* {@link MethodArgumentNotValidException}.
|
||||||
*
|
*
|
||||||
* <p>This {@link HandlerMethodArgumentResolver} should be ordered last as it
|
* <p>This {@link HandlerMethodArgumentResolver} should be ordered last as it
|
||||||
* supports all types and does not require the {@link Payload} annotation.
|
* supports all types and does not require the {@link Payload} annotation.
|
||||||
|
@ -196,8 +199,6 @@ public class PayloadMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the payload if applicable.
|
* Validate the payload if applicable.
|
||||||
* <p>The default implementation checks for {@code @jakarta.validation.Valid},
|
|
||||||
* Spring's {@link Validated}, and custom annotations whose name starts with "Valid".
|
|
||||||
* @param message the currently processed message
|
* @param message the currently processed message
|
||||||
* @param parameter the method parameter
|
* @param parameter the method parameter
|
||||||
* @param target the target payload object
|
* @param target the target payload object
|
||||||
|
@ -208,10 +209,8 @@ public class PayloadMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (Annotation ann : parameter.getParameterAnnotations()) {
|
for (Annotation ann : parameter.getParameterAnnotations()) {
|
||||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
|
||||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
if (validationHints != null) {
|
||||||
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
|
|
||||||
Object[] validationHints = (hints instanceof Object[] objectHints ? objectHints : new Object[] {hints});
|
|
||||||
BeanPropertyBindingResult bindingResult =
|
BeanPropertyBindingResult bindingResult =
|
||||||
new BeanPropertyBindingResult(target, getParameterName(parameter));
|
new BeanPropertyBindingResult(target, getParameterName(parameter));
|
||||||
if (!ObjectUtils.isEmpty(validationHints) && this.validator instanceof SmartValidator sv) {
|
if (!ObjectUtils.isEmpty(validationHints) && this.validator instanceof SmartValidator sv) {
|
||||||
|
|
Loading…
Reference in New Issue