Browse Source

Improve resolution of multi-value async model attrs

Multi-value async attributes like Flux and Observable in the model
are treated with Collection semantics and resolved to Mono<List<?>>
prior to rendering.
pull/1016/merge
Rossen Stoyanchev 8 years ago
parent
commit
a224874b43
  1. 11
      spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java
  2. 19
      spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java

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

@ -294,13 +294,20 @@ public class ViewResolutionResultHandler extends AbstractHandlerResultHandler
private Mono<Void> resolveAsyncAttributes(Map<String, Object> model) { private Mono<Void> resolveAsyncAttributes(Map<String, Object> model) {
List<String> names = new ArrayList<>(); List<String> names = new ArrayList<>();
List<Mono<Object>> valueMonos = new ArrayList<>(); List<Mono<?>> valueMonos = new ArrayList<>();
for (Map.Entry<String, ?> entry : model.entrySet()) { for (Map.Entry<String, ?> entry : model.entrySet()) {
ReactiveAdapter adapter = getAdapterRegistry().getAdapterFrom(null, entry.getValue()); ReactiveAdapter adapter = getAdapterRegistry().getAdapterFrom(null, entry.getValue());
if (adapter != null) { if (adapter != null) {
names.add(entry.getKey()); names.add(entry.getKey());
valueMonos.add(adapter.toMono(entry.getValue()).defaultIfEmpty(NO_VALUE)); if (adapter.getDescriptor().isMultiValue()) {
Flux<Object> value = adapter.toFlux(entry.getValue());
valueMonos.add(value.collectList().defaultIfEmpty(Collections.emptyList()));
}
else {
Mono<Object> value = adapter.toMono(entry.getValue());
valueMonos.add(value.defaultIfEmpty(NO_VALUE));
}
} }
} }

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

@ -33,6 +33,7 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.test.StepVerifier; import reactor.test.StepVerifier;
import rx.Completable; import rx.Completable;
import rx.Observable;
import rx.Single; import rx.Single;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
@ -270,9 +271,11 @@ public class ViewResolutionResultHandlerTests {
@Test @Test
public void modelWithAsyncAttributes() throws Exception { public void modelWithAsyncAttributes() throws Exception {
this.bindingContext.getModel() this.bindingContext.getModel()
.addAttribute("bean1", Mono.just(new TestBean("Bean1"))) .addAttribute("attr1", Mono.just(new TestBean("Bean1")))
.addAttribute("bean2", Single.just(new TestBean("Bean2"))) .addAttribute("attr2", Flux.just(new TestBean("Bean1"), new TestBean("Bean2")))
.addAttribute("empty", Mono.empty()); .addAttribute("attr3", Single.just(new TestBean("Bean2")))
.addAttribute("attr4", Observable.just(new TestBean("Bean1"), new TestBean("Bean2")))
.addAttribute("attr5", Mono.empty());
ResolvableType type = forClass(void.class); ResolvableType type = forClass(void.class);
HandlerResult result = new HandlerResult(new Object(), null, returnType(type), this.bindingContext); HandlerResult result = new HandlerResult(new Object(), null, returnType(type), this.bindingContext);
@ -281,11 +284,13 @@ public class ViewResolutionResultHandlerTests {
this.request.setUri("/account"); this.request.setUri("/account");
handler.handleResult(this.exchange, result).blockMillis(5000); handler.handleResult(this.exchange, result).blockMillis(5000);
assertResponseBody("account: {" + assertResponseBody("account: {" +
"bean1=TestBean[name=Bean1], " + "attr1=TestBean[name=Bean1], " +
"bean2=TestBean[name=Bean2], " + "attr2=[TestBean[name=Bean1], TestBean[name=Bean2]], " +
"org.springframework.validation.BindingResult.bean1=" + "attr3=TestBean[name=Bean2], " +
"attr4=[TestBean[name=Bean1], TestBean[name=Bean2]], " +
"org.springframework.validation.BindingResult.attr1=" +
"org.springframework.validation.BeanPropertyBindingResult: 0 errors, " + "org.springframework.validation.BeanPropertyBindingResult: 0 errors, " +
"org.springframework.validation.BindingResult.bean2=" + "org.springframework.validation.BindingResult.attr3=" +
"org.springframework.validation.BeanPropertyBindingResult: 0 errors" + "org.springframework.validation.BeanPropertyBindingResult: 0 errors" +
"}"); "}");
} }

Loading…
Cancel
Save