BindingResult for resolved async attributes

ViewResolutionResultHandler no longer adds BindingResult objects for
async model attributes. Instead AbstractView adds them later when
those attributes are resolved to concrete values.

Closes gh-22933
This commit is contained in:
Rossen Stoyanchev
2019-05-16 20:41:37 -04:00
parent 172afb510a
commit da4430e27e
4 changed files with 112 additions and 53 deletions

View File

@@ -17,8 +17,8 @@
package org.springframework.web.reactive.result.view;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.reactivex.Observable;
@@ -29,14 +29,15 @@ import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
import org.springframework.mock.web.test.server.MockServerWebExchange;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.validation.BindingResult;
import org.springframework.web.reactive.BindingContext;
import org.springframework.web.server.ServerWebExchange;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.*;
/**
* Unit tests for {@link AbstractView}.
@@ -54,25 +55,31 @@ public class AbstractViewTests {
TestBean testBean1 = new TestBean("Bean1");
TestBean testBean2 = new TestBean("Bean2");
Map<String, Object> attributes = new HashMap<>();
attributes.put("attr1", Mono.just(testBean1).delayElement(Duration.ofMillis(10)));
attributes.put("attr2", Flux.just(testBean1, testBean2).delayElements(Duration.ofMillis(10)));
attributes.put("attr3", Single.just(testBean2));
attributes.put("attr4", Observable.just(testBean1, testBean2));
attributes.put("attr5", Mono.empty());
Map<String, Object> inMap = new HashMap<>();
inMap.put("attr1", Mono.just(testBean1).delayElement(Duration.ofMillis(10)));
inMap.put("attr2", Flux.just(testBean1, testBean2).delayElements(Duration.ofMillis(10)));
inMap.put("attr3", Single.just(testBean2));
inMap.put("attr4", Observable.just(testBean1, testBean2));
inMap.put("attr5", Mono.empty());
this.exchange.getAttributes().put(View.BINDING_CONTEXT_ATTRIBUTE, new BindingContext());
TestView view = new TestView();
StepVerifier.create(
view.render(attributes, null, this.exchange)).verifyComplete();
StepVerifier.create(view.render(inMap, null, this.exchange)).verifyComplete();
Map<String, Object> actual = view.attributes;
assertEquals(testBean1, actual.get("attr1"));
assertArrayEquals(new TestBean[] { testBean1, testBean2 },
((List<TestBean>) actual.get("attr2")).toArray());
assertEquals(testBean2, actual.get("attr3"));
assertArrayEquals(new TestBean[] { testBean1, testBean2 },
((List<TestBean>) actual.get("attr4")).toArray());
assertNull(actual.get("attr5"));
Map<String, Object> outMap = view.attributes;
assertEquals(testBean1, outMap.get("attr1"));
assertEquals(Arrays.asList(testBean1, testBean2), outMap.get("attr2"));
assertEquals(testBean2, outMap.get("attr3"));
assertEquals(Arrays.asList(testBean1, testBean2), outMap.get("attr4"));
assertNull(outMap.get("attr5"));
assertNotNull(outMap.get(BindingResult.MODEL_KEY_PREFIX + "attr1"));
assertNotNull(outMap.get(BindingResult.MODEL_KEY_PREFIX + "attr3"));
assertNull(outMap.get(BindingResult.MODEL_KEY_PREFIX + "attr2"));
assertNull(outMap.get(BindingResult.MODEL_KEY_PREFIX + "attr4"));
assertNull(outMap.get(BindingResult.MODEL_KEY_PREFIX + "attr5"));
}
private static class TestView extends AbstractView {
@@ -81,7 +88,7 @@ public class AbstractViewTests {
@Override
protected Mono<Void> renderInternal(Map<String, Object> renderAttributes,
MediaType contentType, ServerWebExchange exchange) {
@Nullable MediaType contentType, ServerWebExchange exchange) {
this.attributes = renderAttributes;
return Mono.empty();