Skip to content

Commit 801f01e

Browse files
committed
Polishing in ModelAttributeMethodArgumentResolver
See gh-26721
1 parent ea398d7 commit 801f01e

File tree

1 file changed

+47
-58
lines changed

1 file changed

+47
-58
lines changed

Diff for: spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java

+47-58
Original file line numberDiff line numberDiff line change
@@ -110,106 +110,75 @@ public Mono<Object> resolveArgument(
110110
parameter.getGenericParameterType());
111111

112112
String name = ModelInitializer.getNameForParameter(parameter);
113-
Mono<?> valueMono = prepareAttributeMono(name, valueType, context, exchange);
113+
Mono<?> attributeMono = prepareAttributeMono(name, valueType, context, exchange);
114114

115115
// unsafe(): we're intercepting, already serialized Publisher signals
116116
Sinks.One<BindingResult> bindingResultSink = Sinks.unsafe().one();
117117

118118
Map<String, Object> model = context.getModel().asMap();
119119
model.put(BindingResult.MODEL_KEY_PREFIX + name, bindingResultSink.asMono());
120120

121-
return valueMono.flatMap(value -> {
122-
WebExchangeDataBinder binder = context.createDataBinder(exchange, value, name, parameter);
123-
return (bindingDisabled(parameter) ? Mono.empty() : bindRequestParameters(binder, exchange))
121+
return attributeMono.flatMap(attribute -> {
122+
WebExchangeDataBinder binder = context.createDataBinder(exchange, attribute, name, parameter);
123+
return (!bindingDisabled(parameter) ? bindRequestParameters(binder, exchange) : Mono.empty())
124124
.doOnError(bindingResultSink::tryEmitError)
125125
.doOnSuccess(aVoid -> {
126126
validateIfApplicable(binder, parameter, exchange);
127127
BindingResult bindingResult = binder.getBindingResult();
128128
model.put(BindingResult.MODEL_KEY_PREFIX + name, bindingResult);
129-
model.put(name, value);
129+
model.put(name, attribute);
130130
// Ignore result: serialized and buffered (should never fail)
131131
bindingResultSink.tryEmitValue(bindingResult);
132132
})
133133
.then(Mono.fromCallable(() -> {
134134
BindingResult errors = binder.getBindingResult();
135135
if (adapter != null) {
136136
return adapter.fromPublisher(errors.hasErrors() ?
137-
Mono.error(new WebExchangeBindException(parameter, errors)) : valueMono);
137+
Mono.error(new WebExchangeBindException(parameter, errors)) : attributeMono);
138138
}
139139
else {
140140
if (errors.hasErrors() && !hasErrorsArgument(parameter)) {
141141
throw new WebExchangeBindException(parameter, errors);
142142
}
143-
return value;
143+
return attribute;
144144
}
145145
}));
146146
});
147147
}
148148

149-
/**
150-
* Determine if binding should be disabled for the supplied {@link MethodParameter},
151-
* based on the {@link ModelAttribute#binding} annotation attribute.
152-
* @since 5.2.15
153-
*/
154-
private boolean bindingDisabled(MethodParameter parameter) {
155-
ModelAttribute modelAttribute = parameter.getParameterAnnotation(ModelAttribute.class);
156-
return (modelAttribute != null && !modelAttribute.binding());
157-
}
158-
159-
/**
160-
* Extension point to bind the request to the target object.
161-
* @param binder the data binder instance to use for the binding
162-
* @param exchange the current request
163-
* @since 5.2.6
164-
*/
165-
protected Mono<Void> bindRequestParameters(WebExchangeDataBinder binder, ServerWebExchange exchange) {
166-
return binder.bind(exchange);
167-
}
168-
169-
private Mono<?> prepareAttributeMono(String attributeName, ResolvableType attributeType,
170-
BindingContext context, ServerWebExchange exchange) {
149+
private Mono<?> prepareAttributeMono(
150+
String name, ResolvableType type, BindingContext context, ServerWebExchange exchange) {
171151

172-
Object attribute = context.getModel().asMap().get(attributeName);
152+
Object attribute = context.getModel().asMap().get(name);
173153

174154
if (attribute == null) {
175-
attribute = findAndRemoveReactiveAttribute(context.getModel(), attributeName);
155+
attribute = removeReactiveAttribute(context.getModel(), name);
176156
}
177157

178158
if (attribute == null) {
179-
return createAttribute(attributeName, attributeType.toClass(), context, exchange);
159+
return createAttribute(name, type.toClass(), context, exchange);
180160
}
181161

182162
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, attribute);
183-
if (adapter != null) {
184-
Assert.isTrue(!adapter.isMultiValue(), "Data binding only supports single-value async types");
185-
return Mono.from(adapter.toPublisher(attribute));
186-
}
187-
else {
188-
return Mono.justOrEmpty(attribute);
189-
}
163+
Assert.isTrue(adapter == null || !adapter.isMultiValue(), "Model attribute must be single-value publisher");
164+
return (adapter != null ? Mono.from(adapter.toPublisher(attribute)) : Mono.justOrEmpty(attribute));
190165
}
191166

192167
@Nullable
193-
private Object findAndRemoveReactiveAttribute(Model model, String attributeName) {
194-
return model.asMap().entrySet().stream()
195-
.filter(entry -> {
196-
if (!entry.getKey().startsWith(attributeName)) {
197-
return false;
198-
}
199-
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, entry.getValue());
200-
if (adapter == null) {
201-
return false;
168+
private Object removeReactiveAttribute(Model model, String name) {
169+
for (Map.Entry<String, Object> entry : model.asMap().entrySet()) {
170+
if (entry.getKey().startsWith(name)) {
171+
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, entry.getValue());
172+
if (adapter != null) {
173+
if (entry.getKey().equals(name + ClassUtils.getShortName(adapter.getReactiveType()))) {
174+
// Remove since we will be re-inserting the resolved attribute value
175+
model.asMap().remove(entry.getKey());
176+
return entry.getValue();
202177
}
203-
String name = attributeName + ClassUtils.getShortName(adapter.getReactiveType());
204-
return entry.getKey().equals(name);
205-
})
206-
.findFirst()
207-
.map(entry -> {
208-
// Remove since we will be re-inserting the resolved attribute value
209-
model.asMap().remove(entry.getKey());
210-
return entry.getValue();
211-
})
212-
.orElse(null);
178+
}
179+
}
180+
}
181+
return null;
213182
}
214183

215184
private Mono<?> createAttribute(
@@ -274,6 +243,26 @@ public Mono<Map<String, Object>> getValuesToBind(WebExchangeDataBinder binder, S
274243
return binder.getValuesToBind(exchange);
275244
}
276245

246+
/**
247+
* Determine if binding should be disabled for the supplied {@link MethodParameter},
248+
* based on the {@link ModelAttribute#binding} annotation attribute.
249+
* @since 5.2.15
250+
*/
251+
private boolean bindingDisabled(MethodParameter parameter) {
252+
ModelAttribute modelAttribute = parameter.getParameterAnnotation(ModelAttribute.class);
253+
return (modelAttribute != null && !modelAttribute.binding());
254+
}
255+
256+
/**
257+
* Extension point to bind the request to the target object.
258+
* @param binder the data binder instance to use for the binding
259+
* @param exchange the current request
260+
* @since 5.2.6
261+
*/
262+
protected Mono<Void> bindRequestParameters(WebExchangeDataBinder binder, ServerWebExchange exchange) {
263+
return binder.bind(exchange);
264+
}
265+
277266
private boolean hasErrorsArgument(MethodParameter parameter) {
278267
int i = parameter.getParameterIndex();
279268
Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();

0 commit comments

Comments
 (0)