Add MethodParameter[] input to MethodValidationAdapter

This allows re-use of existing MethodParameter instances from controller
methods with cached metadata, and also ensures additional capabilities
such as looking up parameter annotations on interfaces.

See gh-29825
This commit is contained in:
rstoyanchev
2023-06-13 17:40:32 +01:00
parent e7c3e1c516
commit 85d81024a4
8 changed files with 94 additions and 57 deletions

View File

@@ -46,28 +46,16 @@ import org.springframework.web.method.annotation.ModelFactory;
* @author Rossen Stoyanchev
* @since 6.1
*/
public class HandlerMethodValidator extends DefaultMethodValidator {
public final class HandlerMethodValidator extends DefaultMethodValidator {
public HandlerMethodValidator(MethodValidationAdapter adapter) {
private HandlerMethodValidator(MethodValidationAdapter adapter) {
super(adapter);
adapter.setBindingResultNameResolver(this::determineObjectName);
}
private String determineObjectName(MethodParameter param, @Nullable Object argument) {
if (param.hasParameterAnnotation(RequestBody.class) || param.hasParameterAnnotation(RequestPart.class)) {
return Conventions.getVariableNameForParameter(param);
}
else {
return ((param.getParameterIndex() != -1) ?
ModelFactory.getNameForParameter(param) :
ModelFactory.getNameForReturnValue(argument, param));
}
}
@Override
protected void handleArgumentsResult(
protected void handleArgumentsValidationResult(
Object bean, Method method, Object[] arguments, Class<?>[] groups, MethodValidationResult result) {
if (result.getConstraintViolations().isEmpty()) {
@@ -93,12 +81,21 @@ public class HandlerMethodValidator extends DefaultMethodValidator {
result.throwIfViolationsPresent();
}
private String determineObjectName(MethodParameter param, @Nullable Object argument) {
if (param.hasParameterAnnotation(RequestBody.class) || param.hasParameterAnnotation(RequestPart.class)) {
return Conventions.getVariableNameForParameter(param);
}
else {
return ((param.getParameterIndex() != -1) ?
ModelFactory.getNameForParameter(param) :
ModelFactory.getNameForReturnValue(argument, param));
}
}
/**
* Create a {@link MethodValidator} if Bean Validation is enabled in Spring MVC or WebFlux.
* @param bindingInitializer for the configured Validator and MessageCodesResolver
* @param parameterNameDiscoverer the {@code ParameterNameDiscoverer} to use
* for {@link MethodValidationAdapter#setParameterNameDiscoverer}
* Static factory method to create a {@link HandlerMethodValidator} if Bean
* Validation is enabled in Spring MVC or WebFlux.
*/
@Nullable
public static MethodValidator from(
@@ -107,15 +104,17 @@ public class HandlerMethodValidator extends DefaultMethodValidator {
if (bindingInitializer instanceof ConfigurableWebBindingInitializer configurableInitializer) {
if (configurableInitializer.getValidator() instanceof Validator validator) {
MethodValidationAdapter validationAdapter = new MethodValidationAdapter(validator);
MethodValidationAdapter adapter = new MethodValidationAdapter(validator);
if (parameterNameDiscoverer != null) {
validationAdapter.setParameterNameDiscoverer(parameterNameDiscoverer);
adapter.setParameterNameDiscoverer(parameterNameDiscoverer);
}
MessageCodesResolver codesResolver = configurableInitializer.getMessageCodesResolver();
if (codesResolver != null) {
validationAdapter.setMessageCodesResolver(codesResolver);
adapter.setMessageCodesResolver(codesResolver);
}
return new HandlerMethodValidator(validationAdapter);
HandlerMethodValidator methodValidator = new HandlerMethodValidator(adapter);
adapter.setBindingResultNameResolver(methodValidator::determineObjectName);
return methodValidator;
}
}
return null;

View File

@@ -168,13 +168,15 @@ public class InvocableHandlerMethod extends HandlerMethod {
Class<?>[] groups = getValidationGroups();
if (shouldValidateArguments() && this.methodValidator != null) {
this.methodValidator.validateArguments(getBean(), getBridgedMethod(), args, groups);
this.methodValidator.validateArguments(
getBean(), getBridgedMethod(), getMethodParameters(), args, groups);
}
Object returnValue = doInvoke(args);
if (shouldValidateReturnValue() && this.methodValidator != null) {
this.methodValidator.validateReturnValue(getBean(), getBridgedMethod(), returnValue, groups);
this.methodValidator.validateReturnValue(
getBean(), getBridgedMethod(), getReturnType(), returnValue, groups);
}
return returnValue;