Skip to content

Commit e59dced

Browse files
committed
BindingResult inserted before rendering
Issue: SPR-14542
1 parent ae003e8 commit e59dced

File tree

2 files changed

+42
-3
lines changed

2 files changed

+42
-3
lines changed

spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.web.reactive.result.view;
1818

1919
import java.util.ArrayList;
20+
import java.util.Collection;
2021
import java.util.Collections;
2122
import java.util.List;
2223
import java.util.Locale;
@@ -38,11 +39,14 @@
3839
import org.springframework.ui.Model;
3940
import org.springframework.util.ClassUtils;
4041
import org.springframework.util.StringUtils;
42+
import org.springframework.validation.BindingResult;
43+
import org.springframework.web.bind.WebExchangeDataBinder;
4144
import org.springframework.web.bind.annotation.ModelAttribute;
4245
import org.springframework.web.reactive.HandlerResult;
4346
import org.springframework.web.reactive.HandlerResultHandler;
4447
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
4548
import org.springframework.web.reactive.result.AbstractHandlerResultHandler;
49+
import org.springframework.web.reactive.result.method.BindingContext;
4650
import org.springframework.web.server.NotAcceptableStatusException;
4751
import org.springframework.web.server.ServerWebExchange;
4852
import org.springframework.web.util.HttpRequestPathHelper;
@@ -241,6 +245,7 @@ else if (CharSequence.class.isAssignableFrom(clazz) && !hasModelAttributeAnnotat
241245
}
242246

243247
return resolveAsyncAttributes(model.asMap())
248+
.doOnSuccess(aVoid -> addBindingResult(result, exchange))
244249
.then(viewsMono)
245250
.then(views -> render(views, model.asMap(), exchange));
246251
});
@@ -322,6 +327,24 @@ private Mono<Void> resolveAsyncAttributes(Map<String, Object> model) {
322327
.then();
323328
}
324329

330+
private void addBindingResult(HandlerResult result, ServerWebExchange exchange) {
331+
BindingContext context = result.getBindingContext();
332+
Map<String, Object> model = context.getModel().asMap();
333+
model.keySet().stream()
334+
.filter(name -> isBindingCandidate(name, model.get(name)))
335+
.filter(name -> !model.containsKey(BindingResult.MODEL_KEY_PREFIX + name))
336+
.forEach(name -> {
337+
WebExchangeDataBinder binder = context.createDataBinder(exchange, model.get(name), name);
338+
model.put(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
339+
});
340+
}
341+
342+
private boolean isBindingCandidate(String name, Object value) {
343+
return !name.startsWith(BindingResult.MODEL_KEY_PREFIX) && value != null &&
344+
!value.getClass().isArray() && !(value instanceof Collection) &&
345+
!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass());
346+
}
347+
325348
private Mono<? extends Void> render(List<View> views, Map<String, Object> model,
326349
ServerWebExchange exchange) {
327350

spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,12 @@ public void handleReturnValueTypes() throws Exception {
166166

167167
returnType = forClass(TestBean.class);
168168
returnValue = new TestBean("Joe");
169-
String responseBody = "account: {id=123, testBean=TestBean[name=Joe]}";
169+
String responseBody = "account: {" +
170+
"id=123, " +
171+
"org.springframework.validation.BindingResult.testBean=" +
172+
"org.springframework.validation.BeanPropertyBindingResult: 0 errors, " +
173+
"testBean=TestBean[name=Joe]" +
174+
"}";
170175
testHandle("/account", returnType, returnValue, responseBody, resolver);
171176

172177
testHandle("/account", resolvableMethod().annotated(ModelAttribute.class),
@@ -239,7 +244,11 @@ public void contentNegotiation() throws Exception {
239244
.block(Duration.ofSeconds(5));
240245

241246
assertEquals(APPLICATION_JSON, this.response.getHeaders().getContentType());
242-
assertResponseBody("jsonView: {testBean=TestBean[name=Joe]}");
247+
assertResponseBody("jsonView: {" +
248+
"org.springframework.validation.BindingResult.testBean=" +
249+
"org.springframework.validation.BeanPropertyBindingResult: 0 errors, " +
250+
"testBean=TestBean[name=Joe]" +
251+
"}");
243252
}
244253

245254
@Test
@@ -272,7 +281,14 @@ public void modelWithAsyncAttributes() throws Exception {
272281

273282
this.request.setUri("/account");
274283
handler.handleResult(this.exchange, result).blockMillis(5000);
275-
assertResponseBody("account: {bean1=TestBean[name=Bean1], bean2=TestBean[name=Bean2]}");
284+
assertResponseBody("account: {" +
285+
"bean1=TestBean[name=Bean1], " +
286+
"bean2=TestBean[name=Bean2], " +
287+
"org.springframework.validation.BindingResult.bean1=" +
288+
"org.springframework.validation.BeanPropertyBindingResult: 0 errors, " +
289+
"org.springframework.validation.BindingResult.bean2=" +
290+
"org.springframework.validation.BeanPropertyBindingResult: 0 errors" +
291+
"}");
276292
}
277293

278294

0 commit comments

Comments
 (0)