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>.
This commit is contained in:
@@ -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
|
||||
@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
|
||||
});
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user