Polishing in ModelAttributeMethodArgumentResolver
See gh-26721
This commit is contained in:
parent
ea398d7b7e
commit
801f01e23f
|
@ -110,7 +110,7 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||||
parameter.getGenericParameterType());
|
parameter.getGenericParameterType());
|
||||||
|
|
||||||
String name = ModelInitializer.getNameForParameter(parameter);
|
String name = ModelInitializer.getNameForParameter(parameter);
|
||||||
Mono<?> valueMono = prepareAttributeMono(name, valueType, context, exchange);
|
Mono<?> attributeMono = prepareAttributeMono(name, valueType, context, exchange);
|
||||||
|
|
||||||
// unsafe(): we're intercepting, already serialized Publisher signals
|
// unsafe(): we're intercepting, already serialized Publisher signals
|
||||||
Sinks.One<BindingResult> bindingResultSink = Sinks.unsafe().one();
|
Sinks.One<BindingResult> bindingResultSink = Sinks.unsafe().one();
|
||||||
|
@ -118,15 +118,15 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||||
Map<String, Object> model = context.getModel().asMap();
|
Map<String, Object> model = context.getModel().asMap();
|
||||||
model.put(BindingResult.MODEL_KEY_PREFIX + name, bindingResultSink.asMono());
|
model.put(BindingResult.MODEL_KEY_PREFIX + name, bindingResultSink.asMono());
|
||||||
|
|
||||||
return valueMono.flatMap(value -> {
|
return attributeMono.flatMap(attribute -> {
|
||||||
WebExchangeDataBinder binder = context.createDataBinder(exchange, value, name, parameter);
|
WebExchangeDataBinder binder = context.createDataBinder(exchange, attribute, name, parameter);
|
||||||
return (bindingDisabled(parameter) ? Mono.empty() : bindRequestParameters(binder, exchange))
|
return (!bindingDisabled(parameter) ? bindRequestParameters(binder, exchange) : Mono.empty())
|
||||||
.doOnError(bindingResultSink::tryEmitError)
|
.doOnError(bindingResultSink::tryEmitError)
|
||||||
.doOnSuccess(aVoid -> {
|
.doOnSuccess(aVoid -> {
|
||||||
validateIfApplicable(binder, parameter, exchange);
|
validateIfApplicable(binder, parameter, exchange);
|
||||||
BindingResult bindingResult = binder.getBindingResult();
|
BindingResult bindingResult = binder.getBindingResult();
|
||||||
model.put(BindingResult.MODEL_KEY_PREFIX + name, bindingResult);
|
model.put(BindingResult.MODEL_KEY_PREFIX + name, bindingResult);
|
||||||
model.put(name, value);
|
model.put(name, attribute);
|
||||||
// Ignore result: serialized and buffered (should never fail)
|
// Ignore result: serialized and buffered (should never fail)
|
||||||
bindingResultSink.tryEmitValue(bindingResult);
|
bindingResultSink.tryEmitValue(bindingResult);
|
||||||
})
|
})
|
||||||
|
@ -134,82 +134,51 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||||
BindingResult errors = binder.getBindingResult();
|
BindingResult errors = binder.getBindingResult();
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
return adapter.fromPublisher(errors.hasErrors() ?
|
return adapter.fromPublisher(errors.hasErrors() ?
|
||||||
Mono.error(new WebExchangeBindException(parameter, errors)) : valueMono);
|
Mono.error(new WebExchangeBindException(parameter, errors)) : attributeMono);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (errors.hasErrors() && !hasErrorsArgument(parameter)) {
|
if (errors.hasErrors() && !hasErrorsArgument(parameter)) {
|
||||||
throw new WebExchangeBindException(parameter, errors);
|
throw new WebExchangeBindException(parameter, errors);
|
||||||
}
|
}
|
||||||
return value;
|
return attribute;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private Mono<?> prepareAttributeMono(
|
||||||
* Determine if binding should be disabled for the supplied {@link MethodParameter},
|
String name, ResolvableType type, BindingContext context, ServerWebExchange exchange) {
|
||||||
* based on the {@link ModelAttribute#binding} annotation attribute.
|
|
||||||
* @since 5.2.15
|
|
||||||
*/
|
|
||||||
private boolean bindingDisabled(MethodParameter parameter) {
|
|
||||||
ModelAttribute modelAttribute = parameter.getParameterAnnotation(ModelAttribute.class);
|
|
||||||
return (modelAttribute != null && !modelAttribute.binding());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
Object attribute = context.getModel().asMap().get(name);
|
||||||
* Extension point to bind the request to the target object.
|
|
||||||
* @param binder the data binder instance to use for the binding
|
|
||||||
* @param exchange the current request
|
|
||||||
* @since 5.2.6
|
|
||||||
*/
|
|
||||||
protected Mono<Void> bindRequestParameters(WebExchangeDataBinder binder, ServerWebExchange exchange) {
|
|
||||||
return binder.bind(exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Mono<?> prepareAttributeMono(String attributeName, ResolvableType attributeType,
|
|
||||||
BindingContext context, ServerWebExchange exchange) {
|
|
||||||
|
|
||||||
Object attribute = context.getModel().asMap().get(attributeName);
|
|
||||||
|
|
||||||
if (attribute == null) {
|
if (attribute == null) {
|
||||||
attribute = findAndRemoveReactiveAttribute(context.getModel(), attributeName);
|
attribute = removeReactiveAttribute(context.getModel(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attribute == null) {
|
if (attribute == null) {
|
||||||
return createAttribute(attributeName, attributeType.toClass(), context, exchange);
|
return createAttribute(name, type.toClass(), context, exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, attribute);
|
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, attribute);
|
||||||
if (adapter != null) {
|
Assert.isTrue(adapter == null || !adapter.isMultiValue(), "Model attribute must be single-value publisher");
|
||||||
Assert.isTrue(!adapter.isMultiValue(), "Data binding only supports single-value async types");
|
return (adapter != null ? Mono.from(adapter.toPublisher(attribute)) : Mono.justOrEmpty(attribute));
|
||||||
return Mono.from(adapter.toPublisher(attribute));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Mono.justOrEmpty(attribute);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Object findAndRemoveReactiveAttribute(Model model, String attributeName) {
|
private Object removeReactiveAttribute(Model model, String name) {
|
||||||
return model.asMap().entrySet().stream()
|
for (Map.Entry<String, Object> entry : model.asMap().entrySet()) {
|
||||||
.filter(entry -> {
|
if (entry.getKey().startsWith(name)) {
|
||||||
if (!entry.getKey().startsWith(attributeName)) {
|
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, entry.getValue());
|
||||||
return false;
|
if (adapter != null) {
|
||||||
|
if (entry.getKey().equals(name + ClassUtils.getShortName(adapter.getReactiveType()))) {
|
||||||
|
// Remove since we will be re-inserting the resolved attribute value
|
||||||
|
model.asMap().remove(entry.getKey());
|
||||||
|
return entry.getValue();
|
||||||
}
|
}
|
||||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, entry.getValue());
|
}
|
||||||
if (adapter == null) {
|
}
|
||||||
return false;
|
}
|
||||||
}
|
return null;
|
||||||
String name = attributeName + ClassUtils.getShortName(adapter.getReactiveType());
|
|
||||||
return entry.getKey().equals(name);
|
|
||||||
})
|
|
||||||
.findFirst()
|
|
||||||
.map(entry -> {
|
|
||||||
// Remove since we will be re-inserting the resolved attribute value
|
|
||||||
model.asMap().remove(entry.getKey());
|
|
||||||
return entry.getValue();
|
|
||||||
})
|
|
||||||
.orElse(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<?> createAttribute(
|
private Mono<?> createAttribute(
|
||||||
|
@ -274,6 +243,26 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||||
return binder.getValuesToBind(exchange);
|
return binder.getValuesToBind(exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if binding should be disabled for the supplied {@link MethodParameter},
|
||||||
|
* based on the {@link ModelAttribute#binding} annotation attribute.
|
||||||
|
* @since 5.2.15
|
||||||
|
*/
|
||||||
|
private boolean bindingDisabled(MethodParameter parameter) {
|
||||||
|
ModelAttribute modelAttribute = parameter.getParameterAnnotation(ModelAttribute.class);
|
||||||
|
return (modelAttribute != null && !modelAttribute.binding());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension point to bind the request to the target object.
|
||||||
|
* @param binder the data binder instance to use for the binding
|
||||||
|
* @param exchange the current request
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
protected Mono<Void> bindRequestParameters(WebExchangeDataBinder binder, ServerWebExchange exchange) {
|
||||||
|
return binder.bind(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasErrorsArgument(MethodParameter parameter) {
|
private boolean hasErrorsArgument(MethodParameter parameter) {
|
||||||
int i = parameter.getParameterIndex();
|
int i = parameter.getParameterIndex();
|
||||||
Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
|
Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
|
||||||
|
|
Loading…
Reference in New Issue