diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java index aee5abb6e1..62ae5c6621 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandler.java @@ -65,7 +65,6 @@ public class ViewResolutionResultHandler implements HandlerResultHandler, Ordere * @param service for converting other reactive types (e.g. rx.Single) to Mono */ public ViewResolutionResultHandler(List resolvers, ConversionService service) { - Assert.notEmpty(resolvers, "At least one ViewResolver is required."); Assert.notNull(service, "'conversionService' is required."); this.viewResolvers.addAll(resolvers); AnnotationAwareOrderComparator.sort(this.viewResolvers); @@ -183,6 +182,14 @@ public class ViewResolutionResultHandler implements HandlerResultHandler, Ordere } } + /** + * Translate the given request into a default view name. This is useful when + * the application leaves the view name unspecified. + *

The default implementation strips the leading and trailing slash from + * the as well as any extension and uses that as the view name. + * @return the default view name to use; if {@code null} is returned + * processing will result in an IllegalStateException. + */ protected String getDefaultViewName(ServerWebExchange exchange, HandlerResult result) { String path = this.pathHelper.getLookupPathForRequest(exchange); if (path.startsWith("/")) { diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java index eff32fb304..05adf2c9f6 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolutionResultHandlerTests.java @@ -59,7 +59,6 @@ import org.springframework.web.server.session.WebSessionManager; import static org.junit.Assert.*; import static org.mockito.Mockito.mock; - /** * Unit tests for {@link ViewResolutionResultHandler}. * @author Rossen Stoyanchev @@ -71,8 +70,6 @@ public class ViewResolutionResultHandlerTests { private MockServerHttpResponse response; - private ServerWebExchange exchange; - private ModelMap model; private DefaultConversionService conversionService; @@ -80,7 +77,6 @@ public class ViewResolutionResultHandlerTests { @Before public void setUp() throws Exception { - this.exchange = createExchange("/path"); this.model = new ExtendedModelMap().addAttribute("id", "123"); this.conversionService = new DefaultConversionService(); this.conversionService.addConverter(new ReactiveStreamsToRxJava1Converter()); @@ -97,32 +93,31 @@ public class ViewResolutionResultHandlerTests { testSupports("handleSingleView", null); } - private void testSupports(String methodName, Object returnValue) throws NoSuchMethodException { - Method method = TestController.class.getMethod(methodName); - ResolvableType returnType = ResolvableType.forMethodParameter(method, -1); - HandlerResult result = new HandlerResult(new Object(), returnValue, returnType, this.model); - List resolvers = Collections.singletonList(mock(ViewResolver.class)); - ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, this.conversionService); - assertTrue(handler.supports(result)); + @Test + public void order() throws Exception { + TestViewResolver resolver1 = new TestViewResolver(); + TestViewResolver resolver2 = new TestViewResolver(); + resolver1.setOrder(2); + resolver2.setOrder(1); + + assertEquals(Arrays.asList(resolver2, resolver1), + new ViewResolutionResultHandler(Arrays.asList(resolver1, resolver2), this.conversionService) + .getViewResolvers()); } @Test public void viewReference() throws Exception { - TestView view = new TestView("account"); - List resolvers = Collections.singletonList(mock(ViewResolver.class)); - ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, this.conversionService); - handle(this.exchange, handler, view, ResolvableType.forClass(View.class)); + Object value = new TestView("account"); + handle("/path", value, ResolvableType.forClass(View.class)); new TestSubscriber().bindTo(this.response.getBody()) .assertValuesWith(buf -> assertEquals("account: {id=123}", asString(buf))); } @Test - public void viewReferenceMono() throws Exception { - TestView view = new TestView("account"); - List resolvers = Collections.singletonList(mock(ViewResolver.class)); - ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, this.conversionService); - handle(this.exchange, handler, Mono.just(view), methodReturnType("handleMonoView")); + public void viewReferenceInMono() throws Exception { + Object value = Mono.just(new TestView("account")); + handle("/path", value, returnTypeFor("handleMonoView")); new TestSubscriber().bindTo(this.response.getBody()) .assertValuesWith(buf -> assertEquals("account: {id=123}", asString(buf))); @@ -130,11 +125,8 @@ public class ViewResolutionResultHandlerTests { @Test public void viewName() throws Exception { - TestView view = new TestView("account"); - TestViewResolver resolver = new TestViewResolver().addView(view); - List resolvers = Collections.singletonList(resolver); - ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, this.conversionService); - handle(this.exchange, handler, "account", ResolvableType.forClass(String.class)); + Object value = "account"; + handle("/path", value, ResolvableType.forClass(String.class), new TestViewResolver("account")); TestSubscriber subscriber = new TestSubscriber<>(); subscriber.bindTo(this.response.getBody()) @@ -142,12 +134,9 @@ public class ViewResolutionResultHandlerTests { } @Test - public void viewNameMono() throws Exception { - TestView view = new TestView("account"); - TestViewResolver resolver = new TestViewResolver().addView(view); - List resolvers = Collections.singletonList(resolver); - ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, this.conversionService); - handle(this.exchange, handler, Mono.just("account"), methodReturnType("handleMonoString")); + public void viewNameInMono() throws Exception { + Object value = Mono.just("account"); + handle("/path", value, returnTypeFor("handleMonoString"), new TestViewResolver("account")); new TestSubscriber().bindTo(this.response.getBody()) .assertValuesWith(buf -> assertEquals("account: {id=123}", asString(buf))); @@ -155,97 +144,76 @@ public class ViewResolutionResultHandlerTests { @Test public void viewNameWithMultipleResolvers() throws Exception { - TestView view1 = new TestView("account"); - TestView view2 = new TestView("profile"); - TestViewResolver resolver1 = new TestViewResolver().addView(view1); - TestViewResolver resolver2 = new TestViewResolver().addView(view2); - List resolvers = Arrays.asList(resolver1, resolver2); - ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, this.conversionService); - handle(this.exchange, handler, "profile", ResolvableType.forClass(String.class)); + String value = "profile"; + handle("/path", value, ResolvableType.forClass(String.class), + new TestViewResolver("account"), new TestViewResolver("profile")); new TestSubscriber().bindTo(this.response.getBody()) .assertValuesWith(buf -> assertEquals("profile: {id=123}", asString(buf))); } @Test - public void viewNameWithNoMatch() throws Exception { - List resolvers = Collections.singletonList(mock(ViewResolver.class)); - ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, this.conversionService); - TestSubscriber subscriber = handle(this.exchange, handler, "account", ResolvableType.forClass(String.class)); + public void viewNameUnresolved() throws Exception { + TestSubscriber subscriber = handle("/path", "account", ResolvableType.forClass(String.class)); subscriber.assertNoValues(); } @Test - public void viewNameNotSpecified() throws Exception { - TestView view = new TestView("account"); - TestViewResolver resolver = new TestViewResolver().addView(view); - List resolvers = Collections.singletonList(resolver); - ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, this.conversionService); + public void viewNameIsNull() throws Exception { + ViewResolver resolver = new TestViewResolver("account"); - ServerWebExchange exchange = createExchange("/account"); - handle(exchange, handler, null, ResolvableType.forClass(String.class)); + handle("/account", null, ResolvableType.forClass(String.class), resolver); new TestSubscriber().bindTo(this.response.getBody()) .assertValuesWith(buf -> assertEquals("account: {id=123}", asString(buf))); - exchange = createExchange("/account/"); - handle(exchange, handler, null, ResolvableType.forClass(String.class)); + handle("/account/", null, ResolvableType.forClass(String.class), resolver); new TestSubscriber().bindTo(this.response.getBody()) .assertValuesWith(buf -> assertEquals("account: {id=123}", asString(buf))); - exchange = createExchange("/account.123"); - handle(exchange, handler, null, ResolvableType.forClass(String.class)); + handle("/account.123", null, ResolvableType.forClass(String.class), resolver); new TestSubscriber().bindTo(this.response.getBody()) .assertValuesWith(buf -> assertEquals("account: {id=123}", asString(buf))); } @Test - public void viewNameMonoEmpty() throws Exception { - TestView view = new TestView("account"); - TestViewResolver resolver = new TestViewResolver().addView(view); - List resolvers = Collections.singletonList(resolver); - HandlerResultHandler handler = new ViewResolutionResultHandler(resolvers, this.conversionService); - ServerWebExchange exchange = createExchange("/account"); - handle(exchange, handler, Mono.empty(), methodReturnType("handleMonoString")); + public void viewNameIsEmptyMono() throws Exception { + Object value = Mono.empty(); + handle("/account", value, returnTypeFor("handleMonoString"), new TestViewResolver("account")); new TestSubscriber().bindTo(this.response.getBody()) .assertValuesWith(buf -> assertEquals("account: {id=123}", asString(buf))); } - @Test - public void ordered() throws Exception { - TestViewResolver resolver1 = new TestViewResolver(); - TestViewResolver resolver2 = new TestViewResolver(); - List resolvers = Arrays.asList(resolver1, resolver2); - - resolver1.setOrder(2); - resolver2.setOrder(1); - - ViewResolutionResultHandler resultHandler = - new ViewResolutionResultHandler(resolvers, this.conversionService); - - assertEquals(Arrays.asList(resolver2, resolver1), resultHandler.getViewResolvers()); + private void testSupports(String methodName, Object returnValue) throws NoSuchMethodException { + Method method = TestController.class.getMethod(methodName); + ResolvableType returnType = ResolvableType.forMethodParameter(method, -1); + HandlerResult result = new HandlerResult(new Object(), returnValue, returnType, this.model); + List resolvers = Collections.singletonList(mock(ViewResolver.class)); + ViewResolutionResultHandler handler = new ViewResolutionResultHandler(resolvers, this.conversionService); + assertTrue(handler.supports(result)); } + private TestSubscriber handle(String path, Object value, ResolvableType type, + ViewResolver... resolvers) throws URISyntaxException { + + List resolverList = Arrays.asList(resolvers); + HandlerResultHandler handler = new ViewResolutionResultHandler(resolverList, this.conversionService); + HandlerResult handlerResult = new HandlerResult(new Object(), value, type, this.model); - private ServerWebExchange createExchange(String path) throws URISyntaxException { ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI(path)); this.response = new MockServerHttpResponse(); WebSessionManager sessionManager = new DefaultWebSessionManager(); - return new DefaultServerWebExchange(request, this.response, sessionManager); - } + ServerWebExchange exchange = new DefaultServerWebExchange(request, this.response, sessionManager); - private TestSubscriber handle(ServerWebExchange exchange, HandlerResultHandler handler, - Object value, ResolvableType type) { + Mono mono = handler.handleResult(exchange, handlerResult); - HandlerResult result = new HandlerResult(new Object(), value, type, this.model); - Mono mono = handler.handleResult(exchange, result); TestSubscriber subscriber = new TestSubscriber<>(); return subscriber.bindTo(mono).await(Duration.ofSeconds(1)); } - private ResolvableType methodReturnType(String methodName, Class... args) throws NoSuchMethodException { + private ResolvableType returnTypeFor(String methodName, Class... args) throws NoSuchMethodException { Method method = TestController.class.getDeclaredMethod(methodName, args); return ResolvableType.forMethodReturnType(method); } @@ -270,6 +238,10 @@ public class ViewResolutionResultHandlerTests { private int order = Ordered.LOWEST_PRECEDENCE; + public TestViewResolver(String... viewNames) { + Arrays.stream(viewNames).forEach(name -> this.views.put(name, new TestView(name))); + } + public void setOrder(int order) { this.order = order; } @@ -279,11 +251,6 @@ public class ViewResolutionResultHandlerTests { return this.order; } - public TestViewResolver addView(TestView view) { - this.views.put(view.getName(), view); - return this; - } - @Override public Mono resolveViewName(String viewName, Locale locale) { View view = this.views.get(viewName);