From 950edf888a9935213ce6dc04a8d140c05fa9a860 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 18 Oct 2017 11:41:46 -0400 Subject: [PATCH] Support for Map method argument in WebFlux Issue: SPR-16086 --- .../annotation/ModelArgumentResolver.java | 23 ++++-- .../ModelArgumentResolverTests.java | 74 +++++++++++++++++++ 2 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolverTests.java diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolver.java index 5f22179348..cd6fa1a41c 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolver.java @@ -16,6 +16,8 @@ package org.springframework.web.reactive.result.method.annotation; +import java.util.Map; + import org.springframework.core.MethodParameter; import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.ui.Model; @@ -26,7 +28,8 @@ import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentR import org.springframework.web.server.ServerWebExchange; /** - * Resolver for the {@link Model} controller method argument. + * Resolver for a controller method argument of type {@link Model} that can + * also be resolved as a {@link java.util.Map}. * * @author Rossen Stoyanchev * @since 5.0 @@ -41,15 +44,25 @@ public class ModelArgumentResolver extends HandlerMethodArgumentResolverSupport @Override public boolean supportsParameter(MethodParameter parameter) { - return checkParameterTypeNoReactiveWrapper(parameter, Model.class::isAssignableFrom); + return checkParameterTypeNoReactiveWrapper(parameter, + type -> Model.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)); } @Override - public Object resolveArgumentValue(MethodParameter methodParameter, BindingContext context, + public Object resolveArgumentValue(MethodParameter parameter, BindingContext context, ServerWebExchange exchange) { - Assert.isAssignable(Model.class, methodParameter.getParameterType()); - return context.getModel(); + Class type = parameter.getParameterType(); + if (Model.class.isAssignableFrom(type)) { + return context.getModel(); + } + else if (Map.class.isAssignableFrom(type)) { + return context.getModel().asMap(); + } + else { + // Should never happen.. + throw new IllegalStateException("Unexpected method parameter type: " + type); + } } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolverTests.java new file mode 100644 index 0000000000..006d23f8c9 --- /dev/null +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelArgumentResolverTests.java @@ -0,0 +1,74 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.reactive.result.method.annotation; + +import java.time.Duration; +import java.util.Map; + +import org.junit.Test; + +import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; +import org.springframework.mock.web.test.server.MockServerWebExchange; +import org.springframework.ui.Model; +import org.springframework.ui.ModelMap; +import org.springframework.web.method.ResolvableMethod; +import org.springframework.web.reactive.BindingContext; +import org.springframework.web.server.ServerWebExchange; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.get; + +/** + * Unit tests for {@link ModelArgumentResolver}. + * @author Rossen Stoyanchev + */ +public class ModelArgumentResolverTests { + + private final ModelArgumentResolver resolver = new ModelArgumentResolver(new ReactiveAdapterRegistry()); + + private final ServerWebExchange exchange = MockServerWebExchange.from(get("/")); + + private final ResolvableMethod testMethod = ResolvableMethod.on(getClass()).named("handle").build(); + + + @Test + public void supportsParameter() throws Exception { + assertTrue(this.resolver.supportsParameter(this.testMethod.arg(Model.class))); + assertTrue(this.resolver.supportsParameter(this.testMethod.arg(Map.class, String.class, Object.class))); + assertTrue(this.resolver.supportsParameter(this.testMethod.arg(ModelMap.class))); + assertFalse(this.resolver.supportsParameter(this.testMethod.arg(Object.class))); + } + + @Test + public void resolveArgument() throws Exception { + testResolveArgument(this.testMethod.arg(Model.class)); + testResolveArgument(this.testMethod.arg(Map.class, String.class, Object.class)); + testResolveArgument(this.testMethod.arg(ModelMap.class)); + } + + private void testResolveArgument(MethodParameter parameter) { + BindingContext context = new BindingContext(); + Object result = this.resolver.resolveArgument(parameter, context, this.exchange).block(Duration.ZERO); + assertSame(context.getModel(), result); + } + + @SuppressWarnings("unused") + void handle(Model model, Map map, ModelMap modelMap, Object object) {} + +}