Polish and minor fixes in ViewResolutionResultHandler

This commit is contained in:
Rossen Stoyanchev 2017-04-07 16:42:59 -04:00
parent e4c62cc029
commit 3780d040ee
3 changed files with 100 additions and 118 deletions

View File

@ -282,7 +282,7 @@ public class ResolvableMethod {
/**
* Filter on methods with the given name.
*/
public Builder named(String methodName) {
public Builder<T> named(String methodName) {
addFilter("methodName=" + methodName, m -> m.getName().equals(methodName));
return this;
}
@ -292,7 +292,7 @@ public class ResolvableMethod {
* See {@link MvcAnnotationPredicates}.
*/
@SafeVarargs
public final Builder annot(Predicate<Method>... filters) {
public final Builder<T> annot(Predicate<Method>... filters) {
this.filters.addAll(Arrays.asList(filters));
return this;
}
@ -303,7 +303,7 @@ public class ResolvableMethod {
* @see MvcAnnotationPredicates
*/
@SafeVarargs
public final Builder annotPresent(Class<? extends Annotation>... annotationTypes) {
public final Builder<T> annotPresent(Class<? extends Annotation>... annotationTypes) {
String message = "annotationPresent=" + Arrays.toString(annotationTypes);
addFilter(message, method ->
Arrays.stream(annotationTypes).allMatch(annotType ->
@ -315,7 +315,7 @@ public class ResolvableMethod {
* Filter on methods not annotated with the given annotation type.
*/
@SafeVarargs
public final Builder annotNotPresent(Class<? extends Annotation>... annotationTypes) {
public final Builder<T> annotNotPresent(Class<? extends Annotation>... annotationTypes) {
String message = "annotationNotPresent=" + Arrays.toString(annotationTypes);
addFilter(message, method -> {
if (annotationTypes.length != 0) {
@ -334,7 +334,7 @@ public class ResolvableMethod {
* @param returnType the return type
* @param generics optional array of generic types
*/
public Builder returning(Class<?> returnType, Class<?>... generics) {
public Builder<T> returning(Class<?> returnType, Class<?>... generics) {
return returning(toResolvableType(returnType, generics));
}
@ -344,7 +344,7 @@ public class ResolvableMethod {
* @param generic at least one generic type
* @param generics optional extra generic types
*/
public Builder returning(Class<?> returnType, ResolvableType generic, ResolvableType... generics) {
public Builder<T> returning(Class<?> returnType, ResolvableType generic, ResolvableType... generics) {
return returning(toResolvableType(returnType, generic, generics));
}
@ -352,7 +352,7 @@ public class ResolvableMethod {
* Filter on methods returning the given type.
* @param returnType the return type
*/
public Builder returning(ResolvableType returnType) {
public Builder<T> returning(ResolvableType returnType) {
String expected = returnType.toString();
String message = "returnType=" + expected;
addFilter(message, m -> expected.equals(ResolvableType.forMethodReturnType(m).toString()));

View File

@ -157,9 +157,10 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport
}
type = result.getReturnType().getGeneric(0).getRawClass();
}
return (CharSequence.class.isAssignableFrom(type) || View.class.isAssignableFrom(type) ||
return (CharSequence.class.isAssignableFrom(type) || Rendering.class.isAssignableFrom(type) ||
Model.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type) ||
Rendering.class.isAssignableFrom(type) || !BeanUtils.isSimpleProperty(type));
void.class.equals(type) || View.class.isAssignableFrom(type) ||
!BeanUtils.isSimpleProperty(type));
}
private boolean hasModelAnnotation(MethodParameter parameter) {
@ -210,17 +211,6 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport
if (returnValue == NO_VALUE || Void.class.equals(clazz) || void.class.equals(clazz)) {
viewsMono = resolveViews(getDefaultViewName(exchange), locale);
}
else if (Model.class.isAssignableFrom(clazz)) {
model.addAllAttributes(((Model) returnValue).asMap());
viewsMono = resolveViews(getDefaultViewName(exchange), locale);
}
else if (Map.class.isAssignableFrom(clazz)) {
model.addAllAttributes((Map<String, ?>) returnValue);
viewsMono = resolveViews(getDefaultViewName(exchange), locale);
}
else if (View.class.isAssignableFrom(clazz)) {
viewsMono = Mono.just(Collections.singletonList((View) returnValue));
}
else if (CharSequence.class.isAssignableFrom(clazz) && !hasModelAnnotation(parameter)) {
viewsMono = resolveViews(returnValue.toString(), locale);
}
@ -233,6 +223,17 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport
viewsMono = (view instanceof String ? resolveViews((String) view, locale) :
Mono.just(Collections.singletonList((View) view)));
}
else if (Model.class.isAssignableFrom(clazz)) {
model.addAllAttributes(((Model) returnValue).asMap());
viewsMono = resolveViews(getDefaultViewName(exchange), locale);
}
else if (Map.class.isAssignableFrom(clazz) && !hasModelAnnotation(parameter)) {
model.addAllAttributes((Map<String, ?>) returnValue);
viewsMono = resolveViews(getDefaultViewName(exchange), locale);
}
else if (View.class.isAssignableFrom(clazz)) {
viewsMono = Mono.just(Collections.singletonList((View) returnValue));
}
else {
String name = getNameForReturnValue(clazz, parameter);
model.addAttribute(name, returnValue);

View File

@ -32,10 +32,10 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import rx.Completable;
import rx.Single;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
@ -55,8 +55,6 @@ import org.springframework.web.server.ServerWebExchange;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.get;
@ -76,35 +74,43 @@ public class ViewResolutionResultHandlerTests {
@Test
public void supports() throws Exception {
testSupports(on(TestController.class).resolveReturnType(String.class));
testSupports(on(TestController.class).resolveReturnType(View.class));
testSupports(on(TestController.class).resolveReturnType(Mono.class, String.class));
testSupports(on(TestController.class).resolveReturnType(Mono.class, View.class));
testSupports(on(TestController.class).resolveReturnType(Single.class, String.class));
testSupports(on(TestController.class).resolveReturnType(Single.class, View.class));
testSupports(on(TestController.class).resolveReturnType(Mono.class, Void.class));
testSupports(on(TestController.class).resolveReturnType(Completable.class));
testSupports(on(TestController.class).resolveReturnType(Model.class));
testSupports(on(TestController.class).resolveReturnType(Map.class));
testSupports(on(TestController.class).resolveReturnType(TestBean.class));
testSupports(on(TestController.class).resolveReturnType(Rendering.class));
testSupports(on(TestController.class).resolveReturnType(Mono.class, Rendering.class));
testSupports(on(Handler.class).annotPresent(ModelAttribute.class).resolveReturnType(String.class));
testSupports(on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(String.class));
testSupports(on(Handler.class).resolveReturnType(Mono.class, String.class));
testSupports(on(TestController.class).annotPresent(ModelAttribute.class).resolveReturnType());
testSupports(on(Handler.class).resolveReturnType(Rendering.class));
testSupports(on(Handler.class).resolveReturnType(Mono.class, Rendering.class));
testSupports(on(Handler.class).resolveReturnType(View.class));
testSupports(on(Handler.class).resolveReturnType(Mono.class, View.class));
testSupports(on(Handler.class).resolveReturnType(void.class));
testSupports(on(Handler.class).resolveReturnType(Mono.class, Void.class));
testSupports(on(Handler.class).resolveReturnType(Completable.class));
testSupports(on(Handler.class).resolveReturnType(Model.class));
testSupports(on(Handler.class).annotPresent(ModelAttribute.class).resolveReturnType(Map.class));
testSupports(on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(Map.class));
testSupports(on(Handler.class).resolveReturnType(TestBean.class));
testSupports(on(Handler.class).annotPresent(ModelAttribute.class).resolveReturnType(Long.class));
testDoesNotSupport(on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(Long.class));
}
private void testSupports(MethodParameter returnType) {
ViewResolutionResultHandler resultHandler = resultHandler(mock(ViewResolver.class));
HandlerResult handlerResult = new HandlerResult(new Object(), null, returnType, this.bindingContext);
assertTrue(resultHandler.supports(handlerResult));
testSupports(returnType, true);
}
@Test
public void doesNotSupport() throws Exception {
MethodParameter returnType = on(TestController.class).resolveReturnType(Integer.class);
private void testDoesNotSupport(MethodParameter returnType) {
testSupports(returnType, false);
}
private void testSupports(MethodParameter returnType, boolean supports) {
ViewResolutionResultHandler resultHandler = resultHandler(mock(ViewResolver.class));
HandlerResult handlerResult = new HandlerResult(new Object(), null, returnType, this.bindingContext);
assertFalse(resultHandler.supports(handlerResult));
assertEquals(supports, resultHandler.supports(handlerResult));
}
@Test
@ -125,31 +131,45 @@ public class ViewResolutionResultHandlerTests {
MethodParameter returnType;
ViewResolver resolver = new TestViewResolver("account");
returnType = on(TestController.class).resolveReturnType(View.class);
returnType = on(Handler.class).resolveReturnType(View.class);
returnValue = new TestView("account");
testHandle("/path", returnType, returnValue, "account: {id=123}");
returnType = on(TestController.class).resolveReturnType(Mono.class, View.class);
returnType = on(Handler.class).resolveReturnType(Mono.class, View.class);
returnValue = Mono.just(new TestView("account"));
testHandle("/path", returnType, returnValue, "account: {id=123}");
returnType = on(TestController.class).resolveReturnType(String.class);
returnType = on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(String.class);
returnValue = "account";
testHandle("/path", returnType, returnValue, "account: {id=123}", resolver);
returnType = on(TestController.class).resolveReturnType(Mono.class, String.class);
returnType = on(Handler.class).annotPresent(ModelAttribute.class).resolveReturnType(String.class);
returnValue = "123";
testHandle("/account", returnType, returnValue, "account: {id=123, myString=123}", resolver);
returnType = on(Handler.class).resolveReturnType(Mono.class, String.class);
returnValue = Mono.just("account");
testHandle("/path", returnType, returnValue, "account: {id=123}", resolver);
returnType = on(TestController.class).resolveReturnType(Model.class);
returnType = on(Handler.class).resolveReturnType(Model.class);
returnValue = new ConcurrentModel().addAttribute("name", "Joe");
testHandle("/account", returnType, returnValue, "account: {id=123, name=Joe}", resolver);
returnType = on(TestController.class).resolveReturnType(Map.class);
// Work around caching issue...
ResolvableType.clearCache();
returnType = on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(Map.class);
returnValue = Collections.singletonMap("name", "Joe");
testHandle("/account", returnType, returnValue, "account: {id=123, name=Joe}", resolver);
returnType = on(TestController.class).resolveReturnType(TestBean.class);
// Work around caching issue...
ResolvableType.clearCache();
returnType = on(Handler.class).annotPresent(ModelAttribute.class).resolveReturnType(Map.class);
returnValue = Collections.singletonMap("name", "Joe");
testHandle("/account", returnType, returnValue, "account: {id=123, myMap={name=Joe}}", resolver);
returnType = on(Handler.class).resolveReturnType(TestBean.class);
returnValue = new TestBean("Joe");
String responseBody = "account: {id=123, " +
"org.springframework.validation.BindingResult.testBean=" +
@ -157,10 +177,10 @@ public class ViewResolutionResultHandlerTests {
"testBean=TestBean[name=Joe]}";
testHandle("/account", returnType, returnValue, responseBody, resolver);
returnType = on(TestController.class).annotPresent(ModelAttribute.class).resolveReturnType();
testHandle("/account", returnType, 99L, "account: {id=123, num=99}", resolver);
returnType = on(Handler.class).annotPresent(ModelAttribute.class).resolveReturnType(Long.class);
testHandle("/account", returnType, 99L, "account: {id=123, myLong=99}", resolver);
returnType = on(TestController.class).resolveReturnType(Rendering.class);
returnType = on(Handler.class).resolveReturnType(Rendering.class);
HttpStatus status = HttpStatus.UNPROCESSABLE_ENTITY;
returnValue = Rendering.view("account").modelAttribute("a", "a1").status(status).header("h", "h1").build();
String expected = "account: {a=a1, id=123}";
@ -172,7 +192,7 @@ public class ViewResolutionResultHandlerTests {
@Test
public void handleWithMultipleResolvers() throws Exception {
Object returnValue = "profile";
MethodParameter returnType = on(TestController.class).resolveReturnType(String.class);
MethodParameter returnType = on(Handler.class).annotNotPresent(ModelAttribute.class).resolveReturnType(String.class);
ViewResolver[] resolvers = {new TestViewResolver("account"), new TestViewResolver("profile")};
testHandle("/account", returnType, returnValue, "profile: {id=123}", resolvers);
@ -180,10 +200,10 @@ public class ViewResolutionResultHandlerTests {
@Test
public void defaultViewName() throws Exception {
testDefaultViewName(null, on(TestController.class).resolveReturnType(String.class));
testDefaultViewName(Mono.empty(), on(TestController.class).resolveReturnType(Mono.class, String.class));
testDefaultViewName(Mono.empty(), on(TestController.class).resolveReturnType(Mono.class, Void.class));
testDefaultViewName(Completable.complete(), on(TestController.class).resolveReturnType(Completable.class));
testDefaultViewName(null, on(Handler.class).annotPresent(ModelAttribute.class).resolveReturnType(String.class));
testDefaultViewName(Mono.empty(), on(Handler.class).resolveReturnType(Mono.class, String.class));
testDefaultViewName(Mono.empty(), on(Handler.class).resolveReturnType(Mono.class, Void.class));
testDefaultViewName(Completable.complete(), on(Handler.class).resolveReturnType(Completable.class));
}
private void testDefaultViewName(Object returnValue, MethodParameter returnType) throws URISyntaxException {
@ -207,7 +227,7 @@ public class ViewResolutionResultHandlerTests {
@Test
public void unresolvedViewName() throws Exception {
String returnValue = "account";
MethodParameter returnType = on(TestController.class).resolveReturnType(String.class);
MethodParameter returnType = on(Handler.class).annotPresent(ModelAttribute.class).resolveReturnType(String.class);
HandlerResult result = new HandlerResult(new Object(), returnValue, returnType, this.bindingContext);
MockServerWebExchange exchange = get("/path").toExchange();
@ -222,7 +242,7 @@ public class ViewResolutionResultHandlerTests {
@Test
public void contentNegotiation() throws Exception {
TestBean value = new TestBean("Joe");
MethodParameter returnType = on(TestController.class).resolveReturnType(TestBean.class);
MethodParameter returnType = on(Handler.class).resolveReturnType(TestBean.class);
HandlerResult handlerResult = new HandlerResult(new Object(), value, returnType, this.bindingContext);
MockServerWebExchange exchange = get("/account").accept(APPLICATION_JSON).toExchange();
@ -244,7 +264,7 @@ public class ViewResolutionResultHandlerTests {
@Test
public void contentNegotiationWith406() throws Exception {
TestBean value = new TestBean("Joe");
MethodParameter returnType = on(TestController.class).resolveReturnType(TestBean.class);
MethodParameter returnType = on(Handler.class).resolveReturnType(TestBean.class);
HandlerResult handlerResult = new HandlerResult(new Object(), value, returnType, this.bindingContext);
MockServerWebExchange exchange = get("/account").accept(APPLICATION_JSON).toExchange();
@ -381,71 +401,32 @@ public class ViewResolutionResultHandlerTests {
@SuppressWarnings("unused")
private static class TestController {
private static class Handler {
String string() {
return null;
}
String string() { return null; }
Mono<String> monoString() { return null; }
@ModelAttribute("myString") String stringWithAnnotation() { return null; }
View view() {
return null;
}
Rendering rendering() { return null; }
Mono<Rendering> monoRendering() { return null; }
Mono<String> monoString() {
return null;
}
View view() { return null; }
Mono<View> monoView() { return null; }
Mono<View> monoView() {
return null;
}
void voidMethod() { }
Mono<Void> monoVoid() { return null; }
Completable completable() { return null; }
Mono<Void> monoVoid() {
return null;
}
Model model() { return null; }
void voidMethod() {
}
Map<?,?> map() { return null; }
@ModelAttribute("myMap") Map<?,?> mapWithAnnotation() { return null; }
Single<String> singleString() {
return null;
}
TestBean testBean() { return null; }
Single<View> singleView() {
return null;
}
Long longValue() { return null; }
@ModelAttribute("myLong") Long longModelAttribute() { return null; }
Completable completable() {
return null;
}
Model model() {
return null;
}
Map<?,?> map() {
return null;
}
TestBean testBean() {
return null;
}
Integer integer() {
return null;
}
@ModelAttribute("num")
Long longAttribute() {
return null;
}
Rendering rendering() {
return null;
}
Mono<Rendering> monoRendering() {
return null;
}
}
}