diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/condition/ParamsRequestCondition.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/condition/ParamsRequestCondition.java index f1340b9089..ffdb0768d8 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/condition/ParamsRequestCondition.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/condition/ParamsRequestCondition.java @@ -21,6 +21,8 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; +import org.springframework.util.Assert; +import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.server.ServerWebExchange; @@ -141,12 +143,18 @@ public final class ParamsRequestCondition extends AbstractRequestCondition getRequestParams(ServerWebExchange exchange) { + MultiValueMap params = exchange.getRequestParams().subscribe().peek(); + Assert.notNull(params, "Expected form data (if any) to be parsed."); + return params; } } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java index d541c01d59..28694baab4 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/AbstractHandlerMethodMapping.java @@ -260,26 +260,31 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap logger.debug("Looking up handler method for path " + lookupPath); } this.mappingRegistry.acquireReadLock(); + try { - HandlerMethod handlerMethod = null; - try { - handlerMethod = lookupHandlerMethod(lookupPath, exchange); - } - catch (Exception ex) { - return Mono.error(ex); - } - if (logger.isDebugEnabled()) { - if (handlerMethod != null) { - logger.debug("Returning handler method [" + handlerMethod + "]"); - } - else { - logger.debug("Did not find handler method for [" + lookupPath + "]"); - } - } - if (handlerMethod != null) { - handlerMethod = handlerMethod.createWithResolvedBean(); - } - return Mono.justOrEmpty(handlerMethod); + // Ensure form data is parsed for "params" conditions... + return exchange.getRequestParams() + .then(() -> { + HandlerMethod handlerMethod = null; + try { + handlerMethod = lookupHandlerMethod(lookupPath, exchange); + } + catch (Exception ex) { + return Mono.error(ex); + } + if (logger.isDebugEnabled()) { + if (handlerMethod != null) { + logger.debug("Returning handler method [" + handlerMethod + "]"); + } + else { + logger.debug("Did not find handler method for [" + lookupPath + "]"); + } + } + if (handlerMethod != null) { + handlerMethod = handlerMethod.createWithResolvedBean(); + } + return Mono.justOrEmpty(handlerMethod); + }); } finally { this.mappingRegistry.releaseReadLock(); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java index 979ca7b8f2..a9ae9f8612 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java @@ -20,6 +20,8 @@ import java.util.Map; import java.util.Optional; import org.springframework.core.MethodParameter; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestParam; @@ -59,11 +61,17 @@ public class RequestParamMapMethodArgumentResolver implements SyncHandlerMethodA public Optional resolveArgumentValue(MethodParameter parameter, BindingContext context, ServerWebExchange exchange) { - MultiValueMap queryParams = exchange.getRequest().getQueryParams(); - Object value = (isMultiValueMap(parameter) ? queryParams : queryParams.toSingleValueMap()); + MultiValueMap requestParams = getRequestParams(exchange); + Object value = (isMultiValueMap(parameter) ? requestParams : requestParams.toSingleValueMap()); return Optional.of(value); } + private MultiValueMap getRequestParams(ServerWebExchange exchange) { + MultiValueMap params = exchange.getRequestParams().subscribe().peek(); + Assert.notNull(params, "Expected form data (if any) to be parsed."); + return params; + } + private boolean isMultiValueMap(MethodParameter parameter) { Class paramType = parameter.getParameterType(); return MultiValueMap.class.isAssignableFrom(paramType); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolver.java index d1c32325eb..e4c2f2937f 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolver.java @@ -24,6 +24,8 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; import org.springframework.core.convert.converter.Converter; +import org.springframework.util.Assert; +import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ValueConstants; @@ -106,7 +108,7 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueSyncAr protected Optional resolveNamedValue(String name, MethodParameter parameter, ServerWebExchange exchange) { - List paramValues = exchange.getRequest().getQueryParams().get(name); + List paramValues = getRequestParams(exchange).get(name); Object result = null; if (paramValues != null) { result = (paramValues.size() == 1 ? paramValues.get(0) : paramValues); @@ -114,6 +116,12 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueSyncAr return Optional.ofNullable(result); } + private MultiValueMap getRequestParams(ServerWebExchange exchange) { + MultiValueMap params = exchange.getRequestParams().subscribe().peek(); + Assert.notNull(params, "Expected form data (if any) to be parsed."); + return params; + } + @Override protected void handleMissingValue(String name, MethodParameter parameter, ServerWebExchange exchange) { String type = parameter.getNestedParameterType().getSimpleName(); diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/condition/ParamsRequestConditionTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/condition/ParamsRequestConditionTests.java index c2fe14cdc4..97a0f12890 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/condition/ParamsRequestConditionTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/condition/ParamsRequestConditionTests.java @@ -22,9 +22,11 @@ import java.util.Collection; import org.junit.Test; import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; -import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.MockWebSessionManager; @@ -35,9 +37,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.springframework.web.util.UriComponentsBuilder.fromPath; /** - * @author Arjen Poutsma + * Unit tests for {@link ParamsRequestCondition}. + * @author Rossen Stoyanchev */ public class ParamsRequestConditionTests { @@ -52,47 +56,45 @@ public class ParamsRequestConditionTests { @Test public void paramPresent() throws Exception { - ServerWebExchange exchange = createExchange("foo", ""); ParamsRequestCondition condition = new ParamsRequestCondition("foo"); - assertNotNull(condition.getMatchingCondition(exchange)); + assertNotNull(condition.getMatchingCondition(exchangeWithQuery("foo="))); + assertNotNull(condition.getMatchingCondition(exchangeWithFormData("foo="))); } @Test public void paramPresentNoMatch() throws Exception { - ServerWebExchange exchange = createExchange("bar", ""); ParamsRequestCondition condition = new ParamsRequestCondition("foo"); - assertNull(condition.getMatchingCondition(exchange)); + assertNull(condition.getMatchingCondition(exchangeWithQuery("bar="))); + assertNull(condition.getMatchingCondition(exchangeWithFormData("bar="))); } @Test public void paramNotPresent() throws Exception { - ServerWebExchange exchange = createExchange(); - ParamsRequestCondition condition = new ParamsRequestCondition("!foo"); - - assertNotNull(condition.getMatchingCondition(exchange)); + ServerWebExchange exchange = exchange(); + assertNotNull(new ParamsRequestCondition("!foo").getMatchingCondition(exchange)); } @Test public void paramValueMatch() throws Exception { - ServerWebExchange exchange = createExchange("foo", "bar"); ParamsRequestCondition condition = new ParamsRequestCondition("foo=bar"); - assertNotNull(condition.getMatchingCondition(exchange)); + assertNotNull(condition.getMatchingCondition(exchangeWithQuery("foo=bar"))); + assertNotNull(condition.getMatchingCondition(exchangeWithFormData("foo=bar"))); } @Test public void paramValueNoMatch() throws Exception { - ServerWebExchange exchange = createExchange("foo", "bazz"); ParamsRequestCondition condition = new ParamsRequestCondition("foo=bar"); - assertNull(condition.getMatchingCondition(exchange)); + assertNull(condition.getMatchingCondition(exchangeWithQuery("foo=bazz"))); + assertNull(condition.getMatchingCondition(exchangeWithFormData("foo=bazz"))); } @Test public void compareTo() throws Exception { - ServerWebExchange exchange = createExchange(); + ServerWebExchange exchange = exchange(new MockServerHttpRequest(HttpMethod.GET, "/")); ParamsRequestCondition condition1 = new ParamsRequestCondition("foo", "bar", "baz"); ParamsRequestCondition condition2 = new ParamsRequestCondition("foo", "bar"); @@ -114,31 +116,29 @@ public class ParamsRequestConditionTests { assertEquals(2, conditions.size()); } - @Test - public void getMatchingCondition() throws Exception { - ServerWebExchange exchange = createExchange("foo", "bar"); - ParamsRequestCondition condition = new ParamsRequestCondition("foo"); - ParamsRequestCondition result = condition.getMatchingCondition(exchange); - assertEquals(condition, result); - - condition = new ParamsRequestCondition("bar"); - - result = condition.getMatchingCondition(exchange); - assertNull(result); + private ServerWebExchange exchangeWithQuery(String query) throws URISyntaxException { + MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "/"); + MultiValueMap params = fromPath("/").query(query).build().getQueryParams(); + request.getQueryParams().putAll(params); + return exchange(request); } - private ServerWebExchange createExchange() throws URISyntaxException { - return createExchange(null, null); + private ServerWebExchange exchangeWithFormData(String formData) throws URISyntaxException { + MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "/"); + request.getHeaders().setContentType(MediaType.APPLICATION_FORM_URLENCODED); + request.setBody(formData); + return exchange(request); } - private ServerWebExchange createExchange(String paramName, String paramValue) throws URISyntaxException { - ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "/"); - if (paramName != null) { - request.getQueryParams().add(paramName, paramValue); - } - WebSessionManager sessionManager = new MockWebSessionManager(); - return new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager); + private ServerWebExchange exchange() { + return exchange(new MockServerHttpRequest(HttpMethod.GET, "/")); + } + + private ServerWebExchange exchange(ServerHttpRequest request) { + MockServerHttpResponse response = new MockServerHttpResponse(); + WebSessionManager manager = new MockWebSessionManager(); + return new DefaultServerWebExchange(request, response, manager); } } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolverTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolverTests.java index d497eb822f..a29dc7cf91 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolverTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolverTests.java @@ -17,21 +17,21 @@ package org.springframework.web.reactive.result.method.annotation; import java.lang.reflect.Method; +import java.net.URISyntaxException; import java.util.Arrays; import java.util.Collections; import java.util.Map; import org.junit.Before; import org.junit.Test; -import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.server.ServerWebExchange; @@ -42,18 +42,16 @@ import org.springframework.web.server.session.WebSessionManager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.springframework.web.util.UriComponentsBuilder.fromPath; /** * Unit tests for {@link RequestParamMapMethodArgumentResolver}. - * * @author Rossen Stoyanchev */ public class RequestParamMapMethodArgumentResolverTests { private RequestParamMapMethodArgumentResolver resolver; - private ServerWebExchange exchange; - private MethodParameter paramMap; private MethodParameter paramMultiValueMap; private MethodParameter paramNamedMap; @@ -65,10 +63,6 @@ public class RequestParamMapMethodArgumentResolverTests { public void setUp() throws Exception { this.resolver = new RequestParamMapMethodArgumentResolver(); - ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "/"); - WebSessionManager sessionManager = new MockWebSessionManager(); - this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager); - Method method = getClass().getMethod("params", Map.class, MultiValueMap.class, Map.class, Map.class); this.paramMap = new SynthesizingMethodParameter(method, 0); this.paramMultiValueMap = new SynthesizingMethodParameter(method, 1); @@ -86,35 +80,51 @@ public class RequestParamMapMethodArgumentResolverTests { } @Test - public void resolveMapArgument() throws Exception { - String name = "foo"; - String value = "bar"; - this.exchange.getRequest().getQueryParams().set(name, value); - Map expected = Collections.singletonMap(name, value); - - Mono mono = resolver.resolveArgument(paramMap, null, exchange); - Object result = mono.block(); - + public void resolveMapArgumentWithQueryString() throws Exception { + Object result= resolve(this.paramMap, exchangeWithQuery("foo=bar")); assertTrue(result instanceof Map); - assertEquals(expected, result); + assertEquals(Collections.singletonMap("foo", "bar"), result); + } + + @Test + public void resolveMapArgumentWithFormData() throws Exception { + Object result= resolve(this.paramMap, exchangeWithFormData("foo=bar")); + assertTrue(result instanceof Map); + assertEquals(Collections.singletonMap("foo", "bar"), result); } @Test public void resolveMultiValueMapArgument() throws Exception { - String name = "foo"; - String value1 = "bar"; - String value2 = "baz"; - this.exchange.getRequest().getQueryParams().put(name, Arrays.asList(value1, value2)); - - MultiValueMap expected = new LinkedMultiValueMap<>(1); - expected.add(name, value1); - expected.add(name, value2); - - Mono mono = this.resolver.resolveArgument(this.paramMultiValueMap, null, this.exchange); - Object result = mono.block(); + ServerWebExchange exchange = exchangeWithQuery("foo=bar&foo=baz"); + Object result= resolve(this.paramMultiValueMap, exchange); assertTrue(result instanceof MultiValueMap); - assertEquals(expected, result); + assertEquals(Collections.singletonMap("foo", Arrays.asList("bar", "baz")), result); + } + + + private ServerWebExchange exchangeWithQuery(String query) throws URISyntaxException { + MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "/"); + MultiValueMap params = fromPath("/").query(query).build().getQueryParams(); + request.getQueryParams().putAll(params); + return exchange(request); + } + + private ServerWebExchange exchangeWithFormData(String formData) throws URISyntaxException { + MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "/"); + request.getHeaders().setContentType(MediaType.APPLICATION_FORM_URLENCODED); + request.setBody(formData); + return exchange(request); + } + + private ServerWebExchange exchange(ServerHttpRequest request) { + MockServerHttpResponse response = new MockServerHttpResponse(); + WebSessionManager manager = new MockWebSessionManager(); + return new DefaultServerWebExchange(request, response, manager); + } + + private Object resolve(MethodParameter parameter, ServerWebExchange exchange) { + return this.resolver.resolveArgument(parameter, null, exchange).blockMillis(0); } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverTests.java b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverTests.java index bc7e1cc9a6..3e1f431901 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverTests.java @@ -17,7 +17,7 @@ package org.springframework.web.reactive.result.method.annotation; import java.lang.reflect.Method; -import java.util.Arrays; +import java.net.URISyntaxException; import java.util.Map; import java.util.Optional; @@ -32,9 +32,11 @@ import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; +import org.springframework.util.MultiValueMap; import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; @@ -45,7 +47,12 @@ import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.MockWebSessionManager; import org.springframework.web.server.session.WebSessionManager; -import static org.junit.Assert.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.springframework.web.util.UriComponentsBuilder.fromPath; /** * Unit tests for {@link RequestParamMethodArgumentResolver}. @@ -56,8 +63,6 @@ public class RequestParamMethodArgumentResolverTests { private RequestParamMethodArgumentResolver resolver; - private ServerWebExchange exchange; - private MethodParameter paramNamedDefaultValueString; private MethodParameter paramNamedStringArray; private MethodParameter paramNamedMap; @@ -67,7 +72,7 @@ public class RequestParamMethodArgumentResolverTests { private MethodParameter paramNotRequired; private MethodParameter paramOptional; - private BindingContext bindingContext; + private BindingContext bindContext; @Before @SuppressWarnings("ConfusingArgumentToVarargsMethod") @@ -77,10 +82,6 @@ public class RequestParamMethodArgumentResolverTests { ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); Method method = ReflectionUtils.findMethod(getClass(), "handle", (Class[]) null); - ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "/"); - WebSessionManager sessionManager = new MockWebSessionManager(); - this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager); - this.paramNamedDefaultValueString = new SynthesizingMethodParameter(method, 0); this.paramNamedStringArray = new SynthesizingMethodParameter(method, 1); this.paramNamedMap = new SynthesizingMethodParameter(method, 2); @@ -94,7 +95,7 @@ public class RequestParamMethodArgumentResolverTests { ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); initializer.setConversionService(new DefaultFormattingConversionService()); - this.bindingContext = new BindingContext(initializer); + this.bindContext = new BindingContext(initializer); } @@ -115,45 +116,33 @@ public class RequestParamMethodArgumentResolverTests { } @Test - public void resolveString() throws Exception { - String expected = "foo"; - this.exchange.getRequest().getQueryParams().set("name", expected); + public void resolveWithQueryString() throws Exception { + assertEquals("foo", resolve(this.paramNamedDefaultValueString, exchangeWithQuery("name=foo"))); + } - Mono mono = this.resolver.resolveArgument( - this.paramNamedDefaultValueString, this.bindingContext, this.exchange); - - Object result = mono.block(); - assertTrue(result instanceof String); - assertEquals("Invalid result", expected, result); + @Test + public void resolveWithFormData() throws Exception { + assertEquals("foo", resolve(this.paramNamedDefaultValueString, exchangeWithFormData("name=foo"))); } @Test public void resolveStringArray() throws Exception { - String[] expected = {"foo", "bar"}; - this.exchange.getRequest().getQueryParams().put("name", Arrays.asList(expected)); - - Mono mono = this.resolver.resolveArgument( - this.paramNamedStringArray, this.bindingContext, this.exchange); - - Object result = mono.block(); + Object result = resolve(this.paramNamedStringArray, exchangeWithQuery("name=foo&name=bar")); assertTrue(result instanceof String[]); - assertArrayEquals(expected, (String[]) result); + assertArrayEquals(new String[] {"foo", "bar"}, (String[]) result); } @Test public void resolveDefaultValue() throws Exception { - Mono mono = this.resolver.resolveArgument( - this.paramNamedDefaultValueString, this.bindingContext, this.exchange); - - Object result = mono.block(); - assertTrue(result instanceof String); - assertEquals("Invalid result", "bar", result); + Object result = resolve(this.paramNamedDefaultValueString, exchange()); + assertEquals("bar", result); } @Test public void missingRequestParam() throws Exception { + Mono mono = this.resolver.resolveArgument( - this.paramNamedStringArray, this.bindingContext, this.exchange); + this.paramNamedStringArray, this.bindContext, exchange()); StepVerifier.create(mono) .expectNextCount(0) @@ -163,65 +152,41 @@ public class RequestParamMethodArgumentResolverTests { @Test public void resolveSimpleTypeParam() throws Exception { - this.exchange.getRequest().getQueryParams().set("stringNotAnnot", "plainValue"); - Mono mono = this.resolver.resolveArgument( - this.paramStringNotAnnot, this.bindingContext, this.exchange); - - Object result = mono.block(); - assertTrue(result instanceof String); + ServerWebExchange exchange = exchangeWithQuery("stringNotAnnot=plainValue"); + Object result = resolve(this.paramStringNotAnnot, exchange); assertEquals("plainValue", result); } @Test // SPR-8561 public void resolveSimpleTypeParamToNull() throws Exception { - Mono mono = this.resolver.resolveArgument( - this.paramStringNotAnnot, this.bindingContext, this.exchange); - - Object result = mono.block(); - assertNull(result); + assertNull(resolve(this.paramStringNotAnnot, exchange())); } @Test // SPR-10180 public void resolveEmptyValueToDefault() throws Exception { - this.exchange.getRequest().getQueryParams().set("name", ""); - Mono mono = this.resolver.resolveArgument( - this.paramNamedDefaultValueString, this.bindingContext, this.exchange); - - Object result = mono.block(); + ServerWebExchange exchange = exchangeWithQuery("name="); + Object result = resolve(this.paramNamedDefaultValueString, exchange); assertEquals("bar", result); } @Test public void resolveEmptyValueWithoutDefault() throws Exception { - this.exchange.getRequest().getQueryParams().set("stringNotAnnot", ""); - Mono mono = this.resolver.resolveArgument( - this.paramStringNotAnnot, this.bindingContext, this.exchange); - - Object result = mono.block(); - assertEquals("", result); + assertEquals("", resolve(this.paramStringNotAnnot, exchangeWithQuery("stringNotAnnot="))); } @Test public void resolveEmptyValueRequiredWithoutDefault() throws Exception { - this.exchange.getRequest().getQueryParams().set("name", ""); - Mono mono = this.resolver.resolveArgument( - this.paramRequired, this.bindingContext, this.exchange); - - Object result = mono.block(); - assertEquals("", result); + assertEquals("", resolve(this.paramRequired, exchangeWithQuery("name="))); } @Test public void resolveOptionalParamValue() throws Exception { - Mono mono = this.resolver.resolveArgument( - this.paramOptional, this.bindingContext, this.exchange); - - Object result = mono.block(); + ServerWebExchange exchange = exchange(); + Object result = resolve(this.paramOptional, exchange); assertEquals(Optional.empty(), result); - this.exchange.getRequest().getQueryParams().set("name", "123"); - mono = this.resolver.resolveArgument(this.paramOptional, this.bindingContext, this.exchange); - result = mono.block(); + exchange = exchangeWithQuery("name=123"); + result = resolve(this.paramOptional, exchange); assertEquals(Optional.class, result.getClass()); Optional value = (Optional) result; @@ -230,6 +195,35 @@ public class RequestParamMethodArgumentResolverTests { } + private ServerWebExchange exchangeWithQuery(String query) throws URISyntaxException { + MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "/"); + MultiValueMap params = fromPath("/").query(query).build().getQueryParams(); + request.getQueryParams().putAll(params); + return exchange(request); + } + + private ServerWebExchange exchangeWithFormData(String formData) throws URISyntaxException { + MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, "/"); + request.getHeaders().setContentType(MediaType.APPLICATION_FORM_URLENCODED); + request.setBody(formData); + return exchange(request); + } + + private ServerWebExchange exchange() { + return exchange(new MockServerHttpRequest(HttpMethod.GET, "/")); + } + + private ServerWebExchange exchange(ServerHttpRequest request) { + MockServerHttpResponse response = new MockServerHttpResponse(); + WebSessionManager manager = new MockWebSessionManager(); + return new DefaultServerWebExchange(request, response, manager); + } + + private Object resolve(MethodParameter parameter, ServerWebExchange exchange) { + return this.resolver.resolveArgument(parameter, this.bindContext, exchange).blockMillis(0); + } + + @SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"}) public void handle( @RequestParam(name = "name", defaultValue = "bar") String param1, diff --git a/spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java b/spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java index d76c4f7542..2e1e9bde36 100644 --- a/spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java +++ b/spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java @@ -76,11 +76,18 @@ public interface ServerWebExchange { Mono getPrincipal(); /** - * Return the form data from the body of the request or an empty {@code Mono} - * if the Content-Type is not "application/x-www-form-urlencoded". + * Return the form data from the body of the request if the Content-Type is + * {@code "application/x-www-form-urlencoded"} or an empty map. */ Mono> getFormData(); + /** + * Return a combined map that represents both + * {@link ServerHttpRequest#getQueryParams()} and {@link #getFormData()} + * or an empty map. + */ + Mono> getRequestParams(); + /** * Returns {@code true} if the one of the {@code checkNotModified} methods * in this contract were used and they returned true. diff --git a/spring-web/src/main/java/org/springframework/web/server/ServerWebExchangeDecorator.java b/spring-web/src/main/java/org/springframework/web/server/ServerWebExchangeDecorator.java index c044a3a3cd..5e2e32322a 100644 --- a/spring-web/src/main/java/org/springframework/web/server/ServerWebExchangeDecorator.java +++ b/spring-web/src/main/java/org/springframework/web/server/ServerWebExchangeDecorator.java @@ -93,6 +93,11 @@ public class ServerWebExchangeDecorator implements ServerWebExchange { return getDelegate().getFormData(); } + @Override + public Mono> getRequestParams() { + return getDelegate().getRequestParams(); + } + @Override public boolean isNotModified() { return getDelegate().isNotModified(); diff --git a/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java b/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java index 84de4de80c..3e222219f8 100644 --- a/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java +++ b/spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java @@ -38,6 +38,8 @@ import org.springframework.http.codec.FormHttpMessageReader; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; @@ -56,9 +58,13 @@ public class DefaultServerWebExchange implements ServerWebExchange { private static final FormHttpMessageReader FORM_READER = new FormHttpMessageReader(); - private static final ResolvableType MULTIVALUE_TYPE = + private static final ResolvableType FORM_DATA_VALUE_TYPE = ResolvableType.forClassWithGenerics(MultiValueMap.class, String.class, String.class); + private static final Mono> EMPTY_FORM_DATA = + Mono.just(CollectionUtils.unmodifiableMultiValueMap(new LinkedMultiValueMap(0))) + .cache(); + private final ServerHttpRequest request; @@ -70,6 +76,8 @@ public class DefaultServerWebExchange implements ServerWebExchange { private final Mono> formDataMono; + private final Mono> requestParamsMono; + private volatile boolean notModified; @@ -80,10 +88,12 @@ public class DefaultServerWebExchange implements ServerWebExchange { Assert.notNull(response, "'response' is required"); Assert.notNull(response, "'sessionManager' is required"); Assert.notNull(response, "'formReader' is required"); + this.request = request; this.response = response; this.sessionMono = sessionManager.getSession(this).cache(); this.formDataMono = initFormData(request); + this.requestParamsMono = initRequestParams(request, this.formDataMono); } private static Mono> initFormData(ServerHttpRequest request) { @@ -91,13 +101,28 @@ public class DefaultServerWebExchange implements ServerWebExchange { try { contentType = request.getHeaders().getContentType(); if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) { - return FORM_READER.readMono(MULTIVALUE_TYPE, request, Collections.emptyMap()).cache(); + Map hints = Collections.emptyMap(); + return FORM_READER.readMono(FORM_DATA_VALUE_TYPE, request, hints).cache(); } } catch (InvalidMediaTypeException ex) { // Ignore } - return Mono.empty(); + return EMPTY_FORM_DATA; + } + + private static Mono> initRequestParams( + ServerHttpRequest request, Mono> formDataMono) { + + return formDataMono + .map(formData -> { + MultiValueMap result = new LinkedMultiValueMap<>(); + result.putAll(request.getQueryParams()); + result.putAll(formData); + return CollectionUtils.unmodifiableMultiValueMap(result); + }) + .defaultIfEmpty(request.getQueryParams()) + .cache(); } @@ -144,6 +169,11 @@ public class DefaultServerWebExchange implements ServerWebExchange { return this.formDataMono; } + @Override + public Mono> getRequestParams() { + return this.requestParamsMono; + } + @Override public boolean isNotModified() { return this.notModified; diff --git a/spring-web/src/test/java/org/springframework/web/server/adapter/DefaultServerWebExchangeCheckNotModifiedTests.java b/spring-web/src/test/java/org/springframework/web/server/adapter/DefaultServerWebExchangeCheckNotModifiedTests.java index 8f0d08f294..22b59a3963 100644 --- a/spring-web/src/test/java/org/springframework/web/server/adapter/DefaultServerWebExchangeCheckNotModifiedTests.java +++ b/spring-web/src/test/java/org/springframework/web/server/adapter/DefaultServerWebExchangeCheckNotModifiedTests.java @@ -43,7 +43,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** - * Parameterized tests for ServletWebRequest + * "checkNotModified" unit tests for {@link DefaultServerWebExchange}. + * * @author Rossen Stoyanchev */ @RunWith(Parameterized.class) diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/condition/ParamsRequestConditionTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/condition/ParamsRequestConditionTests.java index afbc3aa5aa..728ca08729 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/condition/ParamsRequestConditionTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/condition/ParamsRequestConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -26,6 +26,7 @@ import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition.Para import static org.junit.Assert.*; /** + * Unit tests for {@link ParamsRequestCondition}. * @author Arjen Poutsma */ public class ParamsRequestConditionTests { @@ -36,34 +37,28 @@ public class ParamsRequestConditionTests { assertFalse(new ParamsRequestCondition("foo").equals(new ParamsRequestCondition("bar"))); assertFalse(new ParamsRequestCondition("foo").equals(new ParamsRequestCondition("FOO"))); assertEquals(new ParamsRequestCondition("foo=bar"), new ParamsRequestCondition("foo=bar")); - assertFalse( - new ParamsRequestCondition("foo=bar").equals(new ParamsRequestCondition("FOO=bar"))); + assertFalse(new ParamsRequestCondition("foo=bar").equals(new ParamsRequestCondition("FOO=bar"))); } @Test public void paramPresent() { - ParamsRequestCondition condition = new ParamsRequestCondition("foo"); - MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter("foo", ""); - assertNotNull(condition.getMatchingCondition(request)); + assertNotNull(new ParamsRequestCondition("foo").getMatchingCondition(request)); } @Test public void paramPresentNoMatch() { - ParamsRequestCondition condition = new ParamsRequestCondition("foo"); - MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("bar", ""); - assertNull(condition.getMatchingCondition(request)); + assertNull(new ParamsRequestCondition("foo").getMatchingCondition(request)); } @Test public void paramNotPresent() { ParamsRequestCondition condition = new ParamsRequestCondition("!foo"); - MockHttpServletRequest request = new MockHttpServletRequest(); assertNotNull(condition.getMatchingCondition(request)); @@ -71,22 +66,18 @@ public class ParamsRequestConditionTests { @Test public void paramValueMatch() { - ParamsRequestCondition condition = new ParamsRequestCondition("foo=bar"); - MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter("foo", "bar"); - assertNotNull(condition.getMatchingCondition(request)); + assertNotNull(new ParamsRequestCondition("foo=bar").getMatchingCondition(request)); } @Test public void paramValueNoMatch() { - ParamsRequestCondition condition = new ParamsRequestCondition("foo=bar"); - MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter("foo", "bazz"); - assertNull(condition.getMatchingCondition(request)); + assertNull(new ParamsRequestCondition("foo=bar").getMatchingCondition(request)); } @Test @@ -113,20 +104,4 @@ public class ParamsRequestConditionTests { assertEquals(2, conditions.size()); } - @Test - public void getMatchingCondition() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addParameter("foo", "bar"); - - ParamsRequestCondition condition = new ParamsRequestCondition("foo"); - - ParamsRequestCondition result = condition.getMatchingCondition(request); - assertEquals(condition, result); - - condition = new ParamsRequestCondition("bar"); - - result = condition.getMatchingCondition(request); - assertNull(result); - } - }