Browse Source

Properly handle Mono.empty() for view resolution

This commit ensures correct handling for Mono.empty() return value
where the declared return type is Mono<String> or Mono<View>.
pull/1111/head
Rossen Stoyanchev 9 years ago
parent
commit
eb9fe235fe
  1. 61
      spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/ViewResolverResultHandler.java
  2. 28
      spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/ViewResolverResultHandlerTests.java

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

@ -25,6 +25,7 @@ import reactor.core.publisher.Flux; @@ -25,6 +25,7 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
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;
@ -109,25 +110,29 @@ public class ViewResolverResultHandler implements HandlerResultHandler, Ordered @@ -109,25 +110,29 @@ public class ViewResolverResultHandler implements HandlerResultHandler, Ordered
@Override
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
Mono<?> returnValueMono;
if (this.conversionService.canConvert(result.getReturnValueType().getRawClass(), Mono.class)) {
returnValueMono = this.conversionService.convert(result.getReturnValue().get(), Mono.class);
}
else if (result.getReturnValue().isPresent()) {
returnValueMono = Mono.just(result.getReturnValue().get());
}
else {
Optional<String> viewName = getDefaultViewName(result, exchange);
if (viewName.isPresent()) {
returnValueMono = Mono.just(viewName.get());
Mono<Object> mono;
ResolvableType elementType;
ResolvableType returnType = result.getReturnValueType();
if (this.conversionService.canConvert(returnType.getRawClass(), Mono.class)) {
Optional<Object> optionalValue = result.getReturnValue();
if (optionalValue.isPresent()) {
Mono<?> convertedMono = this.conversionService.convert(optionalValue.get(), Mono.class);
mono = convertedMono.map(o -> o);
}
else {
returnValueMono = Mono.error(new IllegalStateException("Handler [" + result.getHandler() + "] " +
"neither returned a view name nor a View object"));
mono = Mono.empty();
}
elementType = returnType.getGeneric(0);
}
else {
mono = Mono.justOrEmpty(result.getReturnValue());
elementType = returnType;
}
return returnValueMono.then(returnValue -> {
mono = mono.otherwiseIfEmpty(handleMissingReturnValue(exchange, result, elementType));
return mono.then(returnValue -> {
if (returnValue instanceof View) {
Flux<DataBuffer> body = ((View) returnValue).render(result, null, exchange);
return exchange.getResponse().setBody(body);
@ -144,15 +149,33 @@ public class ViewResolverResultHandler implements HandlerResultHandler, Ordered @@ -144,15 +149,33 @@ public class ViewResolverResultHandler implements HandlerResultHandler, Ordered
});
}
else {
// Should not happen
return Mono.error(new IllegalStateException(
"Unexpected return value: " + returnValue.getClass()));
// Eventually for model-related return values (should not happen now)
return Mono.error(new IllegalStateException("Unexpected return value"));
}
});
}
protected Optional<String> getDefaultViewName(HandlerResult result, ServerWebExchange exchange) {
return Optional.empty();
private Mono<Object> handleMissingReturnValue(ServerWebExchange exchange, HandlerResult result,
ResolvableType elementType) {
if (isStringOrViewReference(elementType.getRawClass())) {
String defaultViewName = getDefaultViewName(exchange, result);
if (defaultViewName != null) {
return Mono.just(defaultViewName);
}
else {
return Mono.error(new IllegalStateException("Handler [" + result.getHandler() + "] " +
"neither returned a view name nor a View object"));
}
}
else {
// Eventually for model-related return values (should not happen now)
return Mono.error(new IllegalStateException("Unexpected return value type"));
}
}
protected String getDefaultViewName(ServerWebExchange exchange, HandlerResult result) {
return null;
}
}

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

@ -26,7 +26,6 @@ import java.util.HashMap; @@ -26,7 +26,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
@ -128,7 +127,7 @@ public class ViewResolverResultHandlerTests { @@ -128,7 +127,7 @@ public class ViewResolverResultHandlerTests {
TestView view = new TestView("account");
List<ViewResolver> resolvers = Collections.singletonList(mock(ViewResolver.class));
ViewResolverResultHandler handler = new ViewResolverResultHandler(resolvers, this.conversionService);
handle(handler, Mono.just(view), ResolvableType.forClass(Mono.class));
handle(handler, Mono.just(view), methodReturnType("handleMonoView"));
new TestSubscriber<DataBuffer>().bindTo(this.response.getBody())
.assertValuesWith(buf -> assertEquals("account: {id=123}", asString(buf)));
@ -153,7 +152,7 @@ public class ViewResolverResultHandlerTests { @@ -153,7 +152,7 @@ public class ViewResolverResultHandlerTests {
TestViewResolver resolver = new TestViewResolver().addView(view);
List<ViewResolver> resolvers = Collections.singletonList(resolver);
ViewResolverResultHandler handler = new ViewResolverResultHandler(resolvers, this.conversionService);
handle(handler, Mono.just("account"), ResolvableType.forClass(Mono.class));
handle(handler, Mono.just("account"), methodReturnType("handleMonoString"));
new TestSubscriber<DataBuffer>().bindTo(this.response.getBody())
.assertValuesWith(buf -> assertEquals("account: {id=123}", asString(buf)));
@ -192,6 +191,24 @@ public class ViewResolverResultHandlerTests { @@ -192,6 +191,24 @@ public class ViewResolverResultHandlerTests {
assertThat(ex.getMessage(), endsWith("neither returned a view name nor a View object")));
}
@Test
public void viewNameMonoEmpty() throws Exception {
TestView view = new TestView("account");
TestViewResolver resolver = new TestViewResolver().addView(view);
List<ViewResolver> resolvers = Collections.singletonList(resolver);
HandlerResultHandler handler = new ViewResolverResultHandler(resolvers, this.conversionService) {
@Override
protected String getDefaultViewName(ServerWebExchange exchange, HandlerResult result) {
return "account";
}
};
handle(handler, Mono.empty(), methodReturnType("handleMonoString"));
new TestSubscriber<DataBuffer>().bindTo(this.response.getBody())
.assertValuesWith(buf -> assertEquals("account: {id=123}", asString(buf)));
}
@Test
public void ordered() throws Exception {
TestViewResolver resolver1 = new TestViewResolver();
@ -216,6 +233,11 @@ public class ViewResolverResultHandlerTests { @@ -216,6 +233,11 @@ public class ViewResolverResultHandlerTests {
return subscriber.bindTo(mono).await(Duration.ofSeconds(1));
}
private ResolvableType methodReturnType(String methodName, Class<?>... args) throws NoSuchMethodException {
Method method = TestController.class.getDeclaredMethod(methodName, args);
return ResolvableType.forMethodReturnType(method);
}
private static DataBuffer asDataBuffer(String value) {
ByteBuffer byteBuffer = ByteBuffer.wrap(value.getBytes(UTF_8));
return new DefaultDataBufferAllocator().wrap(byteBuffer);

Loading…
Cancel
Save