diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/AbstractView.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/AbstractView.java index ecf8ab2c62..7e882596f7 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/AbstractView.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/AbstractView.java @@ -24,6 +24,7 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -111,10 +112,10 @@ public abstract class AbstractView implements View, ApplicationContextAware { * @param contentType the content type selected to render with which should * match one of the {@link #getSupportedMediaTypes() supported media types}. * @param exchange the current exchange - * @return + * @return {@code Mono} to represent when and if rendering succeeds */ @Override - public Flux render(HandlerResult result, MediaType contentType, + public Mono render(HandlerResult result, MediaType contentType, ServerWebExchange exchange) { if (logger.isTraceEnabled()) { @@ -151,8 +152,9 @@ public abstract class AbstractView implements View, ApplicationContextAware { * @param renderAttributes combined output Map (never {@code null}), * with dynamic values taking precedence over static attributes * @param exchange current exchange + * @return {@code Mono} to represent when and if rendering succeeds */ - protected abstract Flux renderInternal(Map renderAttributes, + protected abstract Mono renderInternal(Map renderAttributes, ServerWebExchange exchange); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/View.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/View.java index cfc9866ba5..a1689b00ef 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/View.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/View.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Optional; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.MediaType; @@ -53,8 +54,8 @@ public interface View { * @param contentType the content type selected to render with which should * match one of the {@link #getSupportedMediaTypes() supported media types}. * @param exchange the current exchange - * @return the output stream + * @return {@code Mono} to represent when and if rendering succeeds */ - Flux render(HandlerResult result, MediaType contentType, ServerWebExchange exchange); + Mono render(HandlerResult result, MediaType contentType, ServerWebExchange exchange); } 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 52c3676c89..07ca656298 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 @@ -34,7 +34,6 @@ import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.convert.ConversionService; -import org.springframework.core.io.buffer.DataBuffer; import org.springframework.ui.Model; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -184,8 +183,7 @@ public class ViewResolutionResultHandler implements HandlerResultHandler, Ordere return viewMono.then(returnValue -> { if (returnValue instanceof View) { - Flux body = ((View) returnValue).render(result, null, exchange); - return exchange.getResponse().writeWith(body); + return ((View) returnValue).render(result, null, exchange); } else if (returnValue instanceof CharSequence) { String viewName = returnValue.toString(); @@ -194,10 +192,7 @@ public class ViewResolutionResultHandler implements HandlerResultHandler, Ordere .concatMap(resolver -> resolver.resolveViewName(viewName, locale)) .next() .otherwiseIfEmpty(handleUnresolvedViewName(viewName)) - .then(view -> { - Flux body = view.render(result, null, exchange); - return exchange.getResponse().writeWith(body); - }); + .then(view -> view.render(result, null, exchange)); } else { // Should not happen diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerView.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerView.java index 4ed672ece3..61841172c5 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerView.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerView.java @@ -30,6 +30,7 @@ import freemarker.template.SimpleHash; import freemarker.template.Template; import freemarker.template.Version; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactoryUtils; @@ -156,7 +157,7 @@ public class FreeMarkerView extends AbstractUrlBasedView { } @Override - protected Flux renderInternal(Map renderAttributes, ServerWebExchange exchange) { + protected Mono renderInternal(Map renderAttributes, ServerWebExchange exchange) { // Expose all standard FreeMarker hash models. SimpleHash freeMarkerModel = getTemplateModel(renderAttributes, exchange); if (logger.isDebugEnabled()) { @@ -170,12 +171,12 @@ public class FreeMarkerView extends AbstractUrlBasedView { } catch (IOException ex) { String message = "Could not load FreeMarker template for URL [" + getUrl() + "]"; - return Flux.error(new IllegalStateException(message, ex)); + return Mono.error(new IllegalStateException(message, ex)); } catch (Throwable ex) { - return Flux.error(ex); + return Mono.error(ex); } - return Flux.just(dataBuffer); + return exchange.getResponse().writeWith(Flux.just(dataBuffer)); } /** diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/UrlBasedViewResolverTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/UrlBasedViewResolverTests.java index c913427e6e..d521a6e7ef 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/UrlBasedViewResolverTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/UrlBasedViewResolverTests.java @@ -63,8 +63,8 @@ public class UrlBasedViewResolverTests { } @Override - protected Flux renderInternal(Map attributes, ServerWebExchange exchange) { - return Flux.empty(); + protected Mono renderInternal(Map attributes, ServerWebExchange exchange) { + return Mono.empty(); } } 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 492a1b4a55..35132680ea 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 @@ -48,6 +48,7 @@ import org.springframework.http.MediaType; import org.springframework.http.server.reactive.MockServerHttpRequest; import org.springframework.http.server.reactive.MockServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.ui.ExtendedModelMap; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; @@ -324,10 +325,11 @@ public class ViewResolutionResultHandlerTests { } @Override - public Flux render(HandlerResult result, MediaType mediaType, ServerWebExchange exchange) { + public Mono render(HandlerResult result, MediaType mediaType, ServerWebExchange exchange) { String value = this.name + ": " + result.getModel().toString(); assertNotNull(value); - return Flux.just(asDataBuffer(value)); + ServerHttpResponse response = exchange.getResponse(); + return response.writeWith(Flux.just(asDataBuffer(value))); } } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerViewTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerViewTests.java index bb28907238..3ea29a6819 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerViewTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerViewTests.java @@ -19,14 +19,12 @@ import java.net.URI; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Locale; -import java.util.Optional; import freemarker.template.Configuration; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import reactor.core.publisher.Flux; import reactor.core.test.TestSubscriber; import org.springframework.context.ApplicationContextException; @@ -46,7 +44,6 @@ import org.springframework.web.server.session.WebSessionManager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; /** * @author Rossen Stoyanchev @@ -60,6 +57,8 @@ public class FreeMarkerViewTests { private ServerWebExchange exchange; + private MockServerHttpResponse response; + private GenericApplicationContext context; private Configuration freeMarkerConfig; @@ -83,7 +82,7 @@ public class FreeMarkerViewTests { fv.setApplicationContext(this.context); MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/path")); - MockServerHttpResponse response = new MockServerHttpResponse(); + this.response = new MockServerHttpResponse(); WebSessionManager manager = new DefaultWebSessionManager(); this.exchange = new DefaultServerWebExchange(request, response, manager); } @@ -127,10 +126,10 @@ public class FreeMarkerViewTests { ModelMap model = new ExtendedModelMap(); model.addAttribute("hello", "hi FreeMarker"); HandlerResult result = new HandlerResult(new Object(), "", ResolvableType.NONE, model); - Flux flux = view.render(result, null, this.exchange); + view.render(result, null, this.exchange); TestSubscriber subscriber = new TestSubscriber<>(); - subscriber.bindTo(flux).assertValuesWith(dataBuffer -> + subscriber.bindTo(this.response.getBody()).assertValuesWith(dataBuffer -> assertEquals("hi FreeMarker", asString(dataBuffer))); }