Use ResolvableType to create WebDataBinder

This provides more flexibility to pass a targetType even if
a MethodParameter is not available.

See gh-26721
This commit is contained in:
rstoyanchev
2023-06-26 13:11:05 +01:00
parent d37d6688d8
commit 11a416156b
10 changed files with 56 additions and 42 deletions

View File

@@ -116,17 +116,17 @@ public class BindingContext {
}
/**
* Create a binder with a target object and a {@code MethodParameter}.
* Create a binder with a target object and a {@link ResolvableType targetType}.
* If the target is {@code null}, then
* {@link WebExchangeDataBinder#setTargetType targetType} is set.
* @since 6.1
*/
public WebExchangeDataBinder createDataBinder(
ServerWebExchange exchange, @Nullable Object target, String name, @Nullable MethodParameter parameter) {
ServerWebExchange exchange, @Nullable Object target, String name, @Nullable ResolvableType targetType) {
WebExchangeDataBinder dataBinder = new ExtendedWebExchangeDataBinder(target, name);
if (target == null && parameter != null) {
dataBinder.setTargetType(ResolvableType.forMethodParameter(parameter));
if (target == null && targetType != null) {
dataBinder.setTargetType(targetType);
}
if (this.initializer != null) {
@@ -134,8 +134,10 @@ public class BindingContext {
}
dataBinder = initDataBinder(dataBinder, exchange);
if (this.methodValidationApplicable && parameter != null) {
MethodValidationInitializer.initBinder(dataBinder, parameter);
if (this.methodValidationApplicable && targetType != null) {
if (targetType.getSource() instanceof MethodParameter parameter) {
MethodValidationInitializer.initBinder(dataBinder, parameter);
}
}
return dataBinder;

View File

@@ -265,11 +265,12 @@ public abstract class AbstractMessageReaderArgumentResolver extends HandlerMetho
return null;
}
private void validate(Object target, Object[] validationHints, MethodParameter param,
private void validate(Object target, Object[] validationHints, MethodParameter parameter,
BindingContext binding, ServerWebExchange exchange) {
String name = Conventions.getVariableNameForParameter(param);
WebExchangeDataBinder binder = binding.createDataBinder(exchange, target, name, param);
String name = Conventions.getVariableNameForParameter(parameter);
ResolvableType type = ResolvableType.forMethodParameter(parameter);
WebExchangeDataBinder binder = binding.createDataBinder(exchange, target, name, type);
try {
LocaleContextHolder.setLocaleContext(exchange.getLocaleContext());
binder.validate(validationHints);
@@ -278,7 +279,7 @@ public abstract class AbstractMessageReaderArgumentResolver extends HandlerMetho
LocaleContextHolder.resetLocaleContext();
}
if (binder.getBindingResult().hasErrors()) {
throw new WebExchangeBindException(param, binder.getBindingResult());
throw new WebExchangeBindException(parameter, binder.getBindingResult());
}
}

View File

@@ -28,6 +28,7 @@ import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.MethodParameter;
import org.springframework.core.ReactiveAdapter;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
@@ -150,14 +151,15 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
if (value == null) {
value = removeReactiveAttribute(name, context.getModel());
}
ResolvableType type = ResolvableType.forMethodParameter(parameter);
if (value != null) {
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, value);
Assert.isTrue(adapter == null || !adapter.isMultiValue(), "Multi-value publisher is not supported");
return (adapter != null ? Mono.from(adapter.toPublisher(value)) : Mono.just(value))
.map(attr -> context.createDataBinder(exchange, attr, name, parameter));
.map(attr -> context.createDataBinder(exchange, attr, name, type));
}
else {
WebExchangeDataBinder binder = context.createDataBinder(exchange, null, name, parameter);
WebExchangeDataBinder binder = context.createDataBinder(exchange, null, name, type);
return constructAttribute(binder, exchange).thenReturn(binder);
}
}