Add BindingResultNameResolver option

See gh-29825
This commit is contained in:
rstoyanchev
2023-06-07 15:11:21 +01:00
parent 0fef380202
commit 5c5d8e61ae
2 changed files with 83 additions and 16 deletions

View File

@@ -85,6 +85,9 @@ public class MethodValidationAdapter {
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
@Nullable
private BindingResultNameResolver objectNameResolver;
/**
* Create an instance using a default JSR-303 validator underneath.
@@ -157,6 +160,19 @@ public class MethodValidationAdapter {
return this.parameterNameDiscoverer;
}
/**
* Configure a resolver for {@link BindingResult} method parameters to match
* the behavior of the higher level programming model, e.g. how the name of
* {@code @ModelAttribute} or {@code @RequestBody} is determined in Spring MVC.
* <p>If this is not configured, then {@link #createBindingResult} will apply
* default behavior to resolve the name to use.
* behavior applies.
* @param nameResolver the resolver to use
*/
public void setBindingResultNameResolver(BindingResultNameResolver nameResolver) {
this.objectNameResolver = nameResolver;
}
/**
* Use this method determine the validation groups to pass into
@@ -307,6 +323,10 @@ public class MethodValidationAdapter {
/**
* Select an object name and create a {@link BindingResult} for the argument.
* You can configure a {@link #setBindingResultNameResolver(BindingResultNameResolver)
* bindingResultNameResolver} to determine in a way that matches the specific
* programming model, e.g. {@code @ModelAttribute} or {@code @RequestBody} arguments
* in Spring MVC.
* <p>By default, the name is based on the parameter name, or for a return type on
* {@link Conventions#getVariableNameForReturnType(Method, Class, Object)}.
* <p>If a name cannot be determined for any reason, e.g. a return value with
@@ -316,22 +336,30 @@ public class MethodValidationAdapter {
* @return the determined name
*/
private BindingResult createBindingResult(MethodParameter parameter, @Nullable Object argument) {
// TODO: allow external customization via Function (e.g. from @ModelAttribute + Conventions based on type)
String objectName = parameter.getParameterName();
int index = parameter.getParameterIndex();
if (index == -1) {
try {
Method method = parameter.getMethod();
if (method != null) {
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, parameter.getContainingClass());
objectName = Conventions.getVariableNameForReturnType(method, resolvedType, argument);
}
String objectName = null;
if (this.objectNameResolver != null) {
objectName = this.objectNameResolver.resolveName(parameter, argument);
}
else {
if (parameter.getParameterIndex() != -1) {
objectName = parameter.getParameterName();
}
catch (IllegalArgumentException ex) {
// insufficient type information
else {
try {
Method method = parameter.getMethod();
if (method != null) {
Class<?> containingClass = parameter.getContainingClass();
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
objectName = Conventions.getVariableNameForReturnType(method, resolvedType, argument);
}
}
catch (IllegalArgumentException ex) {
// insufficient type information
}
}
}
if (objectName == null) {
int index = parameter.getParameterIndex();
objectName = (parameter.getExecutable().getName() + (index != -1 ? ".arg" + index : ""));
}
BeanPropertyBindingResult result = new BeanPropertyBindingResult(argument, objectName);
@@ -340,6 +368,22 @@ public class MethodValidationAdapter {
}
/**
* Contract to determine the object name of an {@code @Valid} method parameter.
*/
public interface BindingResultNameResolver {
/**
* Determine the name for the given method parameter.
* @param parameter the method parameter
* @param value the argument or return value
* @return the name to use
*/
String resolveName(MethodParameter parameter, @Nullable Object value);
}
/**
* Builds a validation result for a value method parameter with constraints
* declared directly on it.