diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/HandlerMethodArgumentResolverSupport.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/HandlerMethodArgumentResolverSupport.java new file mode 100644 index 0000000000..13a576fecc --- /dev/null +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/HandlerMethodArgumentResolverSupport.java @@ -0,0 +1,136 @@ +/* + * 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; + +import java.lang.annotation.Annotation; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapter; +import org.springframework.core.ReactiveAdapterRegistry; +import org.springframework.util.Assert; + +/** + * Base class for {@link HandlerMethodArgumentResolver} implementations with + * access to a {@code ReactiveAdapterRegistry} and methods to check for + * method parameter support. + * + * @author Rossen Stoyanchev + * @since 5.0 + */ +public abstract class HandlerMethodArgumentResolverSupport { + + private final ReactiveAdapterRegistry adapterRegistry; + + + protected HandlerMethodArgumentResolverSupport(ReactiveAdapterRegistry adapterRegistry) { + Assert.notNull(adapterRegistry, "ReactiveAdapterRegistry is required"); + this.adapterRegistry = adapterRegistry; + } + + + /** + * Return the configured {@link ReactiveAdapterRegistry}. + */ + public ReactiveAdapterRegistry getAdapterRegistry() { + return this.adapterRegistry; + } + + + /** + * Evaluate the {@code Predicate} on the the method parameter type or on + * the generic type within a reactive type wrapper. + */ + protected boolean checkParamType(MethodParameter param, Predicate> predicate) { + Class type = param.getParameterType(); + ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type); + if (adapter != null) { + assertHasValues(adapter, param); + type = param.nested().getNestedParameterType(); + } + return predicate.test(type); + } + + private void assertHasValues(ReactiveAdapter adapter, MethodParameter param) { + if (adapter.isNoValue()) { + throw new IllegalArgumentException( + "No value reactive types not supported: " + param.getGenericParameterType()); + } + } + + /** + * Evaluate the {@code Predicate} on the method parameter type but raise an + * {@code IllegalStateException} if the same matches the generic type + * within a reactive type wrapper. + */ + protected boolean checkParamTypeNoReactiveWrapper(MethodParameter param, Predicate> predicate) { + Class type = param.getParameterType(); + ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type); + if (adapter != null) { + assertHasValues(adapter, param); + type = param.nested().getNestedParameterType(); + } + if (predicate.test(type)) { + if (adapter == null) { + return true; + } + throw getReactiveWrapperError(param); + } + return false; + } + + private IllegalStateException getReactiveWrapperError(MethodParameter param) { + return new IllegalStateException(getClass().getSimpleName() + + " doesn't support reactive type wrapper: " + param.getGenericParameterType()); + } + + /** + * Evaluate the {@code Predicate} on the method parameter type if it has the + * given annotation, nesting within {@link java.util.Optional} if necessary, + * but raise an {@code IllegalStateException} if the same matches the generic + * type within a reactive type wrapper. + */ + protected boolean checkAnnotatedParamNoReactiveWrapper( + MethodParameter param, Class annotationType, + BiPredicate> typePredicate) { + + A annotation = param.getParameterAnnotation(annotationType); + if (annotation == null) { + return false; + } + + param = param.nestedIfOptional(); + Class type = param.getNestedParameterType(); + + ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type); + if (adapter != null) { + assertHasValues(adapter, param); + param = param.nested(); + type = param.getNestedParameterType(); + } + + if (typePredicate.test(annotation, type)) { + if (adapter == null) { + return true; + } + throw getReactiveWrapperError(param); + } + + return false; + } + +} diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java index f434aecaee..afef435f19 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java @@ -42,6 +42,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.support.WebExchangeBindException; import org.springframework.web.bind.support.WebExchangeDataBinder; import org.springframework.web.reactive.BindingContext; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; import org.springframework.web.server.UnsupportedMediaTypeStatusException; @@ -58,12 +59,10 @@ import org.springframework.web.server.UnsupportedMediaTypeStatusException; * @author Rossen Stoyanchev * @since 5.0 */ -public abstract class AbstractMessageReaderArgumentResolver { +public abstract class AbstractMessageReaderArgumentResolver extends HandlerMethodArgumentResolverSupport { private final List> messageReaders; - private final ReactiveAdapterRegistry adapterRegistry; - private final List supportedMediaTypes; @@ -83,10 +82,10 @@ public abstract class AbstractMessageReaderArgumentResolver { protected AbstractMessageReaderArgumentResolver(List> messageReaders, ReactiveAdapterRegistry adapterRegistry) { + super(adapterRegistry); Assert.notEmpty(messageReaders, "At least one HttpMessageReader is required."); Assert.notNull(adapterRegistry, "'adapterRegistry' is required"); this.messageReaders = messageReaders; - this.adapterRegistry = adapterRegistry; this.supportedMediaTypes = messageReaders.stream() .flatMap(converter -> converter.getReadableMediaTypes().stream()) .collect(Collectors.toList()); @@ -100,13 +99,6 @@ public abstract class AbstractMessageReaderArgumentResolver { return this.messageReaders; } - /** - * Return the configured {@link ReactiveAdapterRegistry}. - */ - public ReactiveAdapterRegistry getAdapterRegistry() { - return this.adapterRegistry; - } - protected Mono readBody(MethodParameter bodyParameter, boolean isBodyRequired, BindingContext bindingContext, ServerWebExchange exchange) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java index b9c1a7187e..e41eac2ba4 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueArgumentResolver.java @@ -27,11 +27,13 @@ import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.config.BeanExpressionResolver; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.ui.Model; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.server.ServerErrorException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; @@ -56,7 +58,8 @@ import org.springframework.web.server.ServerWebInputException; * @author Rossen Stoyanchev * @since 5.0 */ -public abstract class AbstractNamedValueArgumentResolver implements HandlerMethodArgumentResolver { +public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodArgumentResolverSupport + implements HandlerMethodArgumentResolver { private final ConfigurableBeanFactory configurableBeanFactory; @@ -69,8 +72,12 @@ public abstract class AbstractNamedValueArgumentResolver implements HandlerMetho * @param beanFactory a bean factory to use for resolving ${...} placeholder * and #{...} SpEL expressions in default values, or {@code null} if default * values are not expected to contain expressions + * @param adapterRegistry for checking reactive type wrappers */ - public AbstractNamedValueArgumentResolver(ConfigurableBeanFactory beanFactory) { + public AbstractNamedValueArgumentResolver(ConfigurableBeanFactory beanFactory, + ReactiveAdapterRegistry adapterRegistry) { + + super(adapterRegistry); this.configurableBeanFactory = beanFactory; this.expressionContext = (beanFactory != null ? new BeanExpressionContext(beanFactory, null) : null); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueSyncArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueSyncArgumentResolver.java index 6cdefb1193..ed24987cac 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueSyncArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractNamedValueSyncArgumentResolver.java @@ -22,6 +22,7 @@ import reactor.core.publisher.Mono; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver; import org.springframework.web.server.ServerWebExchange; @@ -39,8 +40,16 @@ public abstract class AbstractNamedValueSyncArgumentResolver extends AbstractNam implements SyncHandlerMethodArgumentResolver { - protected AbstractNamedValueSyncArgumentResolver(ConfigurableBeanFactory beanFactory) { - super(beanFactory); + /** + * @param beanFactory a bean factory to use for resolving ${...} + * placeholder and #{...} SpEL expressions in default values; + * or {@code null} if default values are not expected to have expressions + * @param adapterRegistry for checking reactive type wrappers + */ + protected AbstractNamedValueSyncArgumentResolver(ConfigurableBeanFactory beanFactory, + ReactiveAdapterRegistry adapterRegistry) { + + super(beanFactory, adapterRegistry); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/CookieValueMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/CookieValueMethodArgumentResolver.java index 008d73de6a..78dd40e389 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/CookieValueMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/CookieValueMethodArgumentResolver.java @@ -20,6 +20,7 @@ import java.util.Optional; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.http.HttpCookie; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.server.ServerWebExchange; @@ -42,15 +43,18 @@ public class CookieValueMethodArgumentResolver extends AbstractNamedValueSyncArg * @param beanFactory a bean factory to use for resolving ${...} * placeholder and #{...} SpEL expressions in default values; * or {@code null} if default values are not expected to contain expressions + * @param adapterRegistry for checking reactive type wrappers */ - public CookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { - super(beanFactory); + public CookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory, + ReactiveAdapterRegistry adapterRegistry) { + + super(beanFactory, adapterRegistry); } @Override - public boolean supportsParameter(MethodParameter parameter) { - return parameter.hasParameterAnnotation(CookieValue.class); + public boolean supportsParameter(MethodParameter param) { + return checkAnnotatedParamNoReactiveWrapper(param, CookieValue.class, (annot, type) -> true); } @Override diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ErrorsMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ErrorsMethodArgumentResolver.java index 6318cb3b54..5c84ef0dcd 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ErrorsMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ErrorsMethodArgumentResolver.java @@ -30,6 +30,7 @@ import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.server.ServerWebExchange; /** @@ -40,33 +41,18 @@ import org.springframework.web.server.ServerWebExchange; * @author Rossen Stoyanchev * @since 5.0 */ -public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolver { +public class ErrorsMethodArgumentResolver extends HandlerMethodArgumentResolverSupport + implements HandlerMethodArgumentResolver { - private final ReactiveAdapterRegistry adapterRegistry; - - /** - * Class constructor. - * @param registry for adapting to other reactive types from and to Mono - */ public ErrorsMethodArgumentResolver(ReactiveAdapterRegistry registry) { - Assert.notNull(registry, "'ReactiveAdapterRegistry' is required."); - this.adapterRegistry = registry; - } - - - /** - * Return the configured {@link ReactiveAdapterRegistry}. - */ - public ReactiveAdapterRegistry getAdapterRegistry() { - return this.adapterRegistry; + super(registry); } @Override public boolean supportsParameter(MethodParameter parameter) { - Class clazz = parameter.getParameterType(); - return Errors.class.isAssignableFrom(clazz); + return checkParamTypeNoReactiveWrapper(parameter, Errors.class::isAssignableFrom); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ExpressionValueMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ExpressionValueMethodArgumentResolver.java index bd36f972a1..3ac65ecec1 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ExpressionValueMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ExpressionValueMethodArgumentResolver.java @@ -21,6 +21,7 @@ import java.util.Optional; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.web.server.ServerWebExchange; /** @@ -40,15 +41,18 @@ public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueSyn * @param beanFactory a bean factory to use for resolving ${...} * placeholder and #{...} SpEL expressions in default values; * or {@code null} if default values are not expected to contain expressions + * @param adapterRegistry for checking reactive type wrappers */ - public ExpressionValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { - super(beanFactory); + public ExpressionValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory, + ReactiveAdapterRegistry adapterRegistry) { + + super(beanFactory, adapterRegistry); } @Override - public boolean supportsParameter(MethodParameter parameter) { - return parameter.hasParameterAnnotation(Value.class); + public boolean supportsParameter(MethodParameter param) { + return checkAnnotatedParamNoReactiveWrapper(param, Value.class, (annot, type) -> true); } @Override diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java index 7956317b23..d4e211f248 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolver.java @@ -26,7 +26,6 @@ import org.springframework.http.HttpEntity; import org.springframework.http.RequestEntity; import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.validation.Validator; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; import org.springframework.web.server.ServerWebExchange; @@ -42,19 +41,7 @@ import org.springframework.web.server.ServerWebExchange; public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentResolver implements HandlerMethodArgumentResolver { - /** - * Constructor with {@link HttpMessageReader}'s and a {@link Validator}. - * @param readers readers for de-serializing the request body with - */ - public HttpEntityArgumentResolver(List> readers) { - super(readers); - } - /** - * Constructor that also accepts a {@link ReactiveAdapterRegistry}. - * @param readers readers for de-serializing the request body with - * @param registry for adapting to other reactive types from Flux and Mono - */ public HttpEntityArgumentResolver(List> readers, ReactiveAdapterRegistry registry) { super(readers, registry); } @@ -62,8 +49,8 @@ public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentRes @Override public boolean supportsParameter(MethodParameter parameter) { - Class clazz = parameter.getParameterType(); - return (HttpEntity.class.equals(clazz) || RequestEntity.class.equals(clazz)); + return checkParamTypeNoReactiveWrapper(parameter, + type -> HttpEntity.class.equals(type) || RequestEntity.class.equals(type)); } @Override 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 99689f5bbc..4c97b57303 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 @@ -19,8 +19,11 @@ package org.springframework.web.reactive.result.method.annotation; import java.util.Optional; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.ui.Model; +import org.springframework.util.Assert; import org.springframework.web.reactive.BindingContext; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver; import org.springframework.web.server.ServerWebExchange; @@ -30,18 +33,25 @@ import org.springframework.web.server.ServerWebExchange; * @author Rossen Stoyanchev * @since 5.0 */ -public class ModelArgumentResolver implements SyncHandlerMethodArgumentResolver { +public class ModelArgumentResolver extends HandlerMethodArgumentResolverSupport + implements SyncHandlerMethodArgumentResolver { + + + public ModelArgumentResolver(ReactiveAdapterRegistry adapterRegistry) { + super(adapterRegistry); + } @Override public boolean supportsParameter(MethodParameter parameter) { - return Model.class.isAssignableFrom(parameter.getParameterType()); + return checkParamTypeNoReactiveWrapper(parameter, Model.class::isAssignableFrom); } @Override public Optional resolveArgumentValue(MethodParameter methodParameter, BindingContext context, ServerWebExchange exchange) { + Assert.isAssignable(Model.class, methodParameter.getParameterType()); return Optional.of(context.getModel()); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java index a4e21ae043..89c50fdf54 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java @@ -40,6 +40,7 @@ import org.springframework.web.bind.support.WebExchangeBindException; import org.springframework.web.bind.support.WebExchangeDataBinder; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.server.ServerWebExchange; /** @@ -59,42 +60,24 @@ import org.springframework.web.server.ServerWebExchange; * @author Rossen Stoyanchev * @since 5.0 */ -public class ModelAttributeMethodArgumentResolver implements HandlerMethodArgumentResolver { - - private final ReactiveAdapterRegistry adapterRegistry; +public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentResolverSupport + implements HandlerMethodArgumentResolver { private final boolean useDefaultResolution; - /** - * Class constructor. - * @param registry for adapting to other reactive types from and to Mono - */ - public ModelAttributeMethodArgumentResolver(ReactiveAdapterRegistry registry) { - this(registry, false); - } - /** * Class constructor with a default resolution mode flag. - * @param registry for adapting to other reactive types from and to Mono + * @param adapterRegistry for adapting to other reactive types from and to Mono * @param useDefaultResolution if "true", non-simple method arguments and * return values are considered model attributes with or without a * {@code @ModelAttribute} annotation present. */ - public ModelAttributeMethodArgumentResolver(ReactiveAdapterRegistry registry, + public ModelAttributeMethodArgumentResolver(ReactiveAdapterRegistry adapterRegistry, boolean useDefaultResolution) { - Assert.notNull(registry, "'ReactiveAdapterRegistry' is required."); + super(adapterRegistry); this.useDefaultResolution = useDefaultResolution; - this.adapterRegistry = registry; - } - - - /** - * Return the configured {@link ReactiveAdapterRegistry}. - */ - public ReactiveAdapterRegistry getAdapterRegistry() { - return this.adapterRegistry; } @@ -103,16 +86,8 @@ public class ModelAttributeMethodArgumentResolver implements HandlerMethodArgume if (parameter.hasParameterAnnotation(ModelAttribute.class)) { return true; } - if (this.useDefaultResolution) { - Class clazz = parameter.getParameterType(); - ReactiveAdapter adapter = getAdapterRegistry().getAdapter(clazz); - if (adapter != null) { - if (adapter.isNoValue() || adapter.isMultiValue()) { - return false; - } - clazz = parameter.nested().getNestedParameterType(); - } - return !BeanUtils.isSimpleProperty(clazz); + else if (this.useDefaultResolution) { + return checkParamType(parameter, type -> !BeanUtils.isSimpleProperty(type)); } return false; } @@ -125,6 +100,10 @@ public class ModelAttributeMethodArgumentResolver implements HandlerMethodArgume ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type.resolve()); ResolvableType valueType = (adapter != null ? type.getGeneric(0) : type); + Assert.state(adapter == null || !adapter.isMultiValue(), + getClass().getSimpleName() + " doesn't support multi-value reactive type wrapper: " + + parameter.getGenericParameterType()); + String name = getAttributeName(valueType, parameter); Mono valueMono = getAttributeMono(name, valueType, context.getModel()); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolver.java index de235e4d02..eb220cac59 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolver.java @@ -21,10 +21,12 @@ import java.util.Map; import java.util.Optional; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver; import org.springframework.web.server.ServerWebExchange; @@ -38,17 +40,25 @@ import org.springframework.web.server.ServerWebExchange; * @since 5.0 * @see PathVariableMethodArgumentResolver */ -public class PathVariableMapMethodArgumentResolver implements SyncHandlerMethodArgumentResolver { +public class PathVariableMapMethodArgumentResolver extends HandlerMethodArgumentResolverSupport + implements SyncHandlerMethodArgumentResolver { + + + public PathVariableMapMethodArgumentResolver(ReactiveAdapterRegistry adapterRegistry) { + super(adapterRegistry); + } @Override public boolean supportsParameter(MethodParameter parameter) { - PathVariable annotation = parameter.getParameterAnnotation(PathVariable.class); - return (annotation != null && - Map.class.isAssignableFrom(parameter.getParameterType()) && - !StringUtils.hasText(annotation.value())); + return checkAnnotatedParamNoReactiveWrapper(parameter, PathVariable.class, this::allVariables); } + private boolean allVariables(PathVariable pathVariable, Class type) { + return Map.class.isAssignableFrom(type) && !StringUtils.hasText(pathVariable.value()); + } + + @Override public Optional resolveArgumentValue(MethodParameter methodParameter, BindingContext context, ServerWebExchange exchange) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMethodArgumentResolver.java index 64f83c5700..2a936afaf9 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PathVariableMethodArgumentResolver.java @@ -21,6 +21,7 @@ import java.util.Optional; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.convert.converter.Converter; import org.springframework.ui.Model; import org.springframework.util.StringUtils; @@ -52,21 +53,26 @@ import org.springframework.web.server.ServerWebExchange; public class PathVariableMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver { - public PathVariableMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { - super(beanFactory); + /** + * @param beanFactory a bean factory to use for resolving ${...} + * placeholder and #{...} SpEL expressions in default values; + * or {@code null} if default values are not expected to contain expressions + * @param adapterRegistry for checking reactive type wrappers + */ + public PathVariableMethodArgumentResolver(ConfigurableBeanFactory beanFactory, + ReactiveAdapterRegistry adapterRegistry) { + + super(beanFactory, adapterRegistry); } @Override public boolean supportsParameter(MethodParameter parameter) { - if (!parameter.hasParameterAnnotation(PathVariable.class)) { - return false; - } - if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { - String paramName = parameter.getParameterAnnotation(PathVariable.class).value(); - return StringUtils.hasText(paramName); - } - return true; + return checkAnnotatedParamNoReactiveWrapper(parameter, PathVariable.class, this::singlePathVariable); + } + + private boolean singlePathVariable(PathVariable pathVariable, Class type) { + return !Map.class.isAssignableFrom(type) || StringUtils.hasText(pathVariable.name()); } @Override diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PrincipalArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PrincipalArgumentResolver.java index 9de42f8aa2..54559ce9f7 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PrincipalArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/PrincipalArgumentResolver.java @@ -21,8 +21,11 @@ import java.security.Principal; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; +import org.springframework.util.Assert; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.server.ServerWebExchange; /** @@ -32,26 +35,26 @@ import org.springframework.web.server.ServerWebExchange; * @since 5.0 * @see ServerWebExchangeArgumentResolver */ -public class PrincipalArgumentResolver implements HandlerMethodArgumentResolver { +public class PrincipalArgumentResolver extends HandlerMethodArgumentResolverSupport + implements HandlerMethodArgumentResolver { + + + public PrincipalArgumentResolver(ReactiveAdapterRegistry adapterRegistry) { + super(adapterRegistry); + } + @Override public boolean supportsParameter(MethodParameter parameter) { - return (Principal.class.isAssignableFrom(parameter.getParameterType())); + return checkParamTypeNoReactiveWrapper(parameter, Principal.class::isAssignableFrom); } @Override public Mono resolveArgument(MethodParameter parameter, BindingContext context, ServerWebExchange exchange) { - Class paramType = parameter.getParameterType(); - if (Principal.class.isAssignableFrom(paramType)) { - return exchange.getPrincipal().cast(Object.class); - } - else { - // should never happen... - throw new IllegalArgumentException( - "Unknown parameter type: " + paramType + " in method: " + parameter.getMethod()); - } + Assert.isAssignable(Principal.class, parameter.getParameterType()); + return exchange.getPrincipal().cast(Object.class); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestAttributeMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestAttributeMethodArgumentResolver.java index 3b5b86f141..079335c2f6 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestAttributeMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestAttributeMethodArgumentResolver.java @@ -19,6 +19,7 @@ import java.util.Optional; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.server.ServerWebExchange; @@ -34,14 +35,22 @@ import org.springframework.web.server.ServerWebInputException; public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver { - public RequestAttributeMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { - super(beanFactory); + /** + * @param beanFactory a bean factory to use for resolving ${...} + * placeholder and #{...} SpEL expressions in default values; + * or {@code null} if default values are not expected to have expressions + * @param adapterRegistry for checking reactive type wrappers + */ + public RequestAttributeMethodArgumentResolver(ConfigurableBeanFactory beanFactory, + ReactiveAdapterRegistry adapterRegistry) { + + super(beanFactory, adapterRegistry); } @Override - public boolean supportsParameter(MethodParameter parameter) { - return parameter.hasParameterAnnotation(RequestAttribute.class); + public boolean supportsParameter(MethodParameter param) { + return checkAnnotatedParamNoReactiveWrapper(param, RequestAttribute.class, (annot, type) -> true); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java index 414f1b2792..9753b4a2f4 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolver.java @@ -47,19 +47,7 @@ import org.springframework.web.server.ServerWebInputException; public class RequestBodyArgumentResolver extends AbstractMessageReaderArgumentResolver implements HandlerMethodArgumentResolver { - /** - * Constructor with {@link HttpMessageReader}'s and a {@link Validator}. - * @param readers readers for de-serializing the request body with - */ - public RequestBodyArgumentResolver(List> readers) { - super(readers); - } - /** - * Constructor that also accepts a {@link ReactiveAdapterRegistry}. - * @param readers readers for de-serializing the request body with - * @param registry for adapting to other reactive types from Flux and Mono - */ public RequestBodyArgumentResolver(List> readers, ReactiveAdapterRegistry registry) { super(readers, registry); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolver.java index 33cf22e930..d8d4e42575 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolver.java @@ -20,10 +20,12 @@ import java.util.Map; import java.util.Optional; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.http.HttpHeaders; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.reactive.BindingContext; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver; import org.springframework.web.server.ServerWebExchange; @@ -40,15 +42,25 @@ import org.springframework.web.server.ServerWebExchange; * @since 5.0 * @see RequestHeaderMethodArgumentResolver */ -public class RequestHeaderMapMethodArgumentResolver implements SyncHandlerMethodArgumentResolver { +public class RequestHeaderMapMethodArgumentResolver extends HandlerMethodArgumentResolverSupport + implements SyncHandlerMethodArgumentResolver { + + + public RequestHeaderMapMethodArgumentResolver(ReactiveAdapterRegistry adapterRegistry) { + super(adapterRegistry); + } @Override - public boolean supportsParameter(MethodParameter parameter) { - return (parameter.hasParameterAnnotation(RequestHeader.class) && - Map.class.isAssignableFrom(parameter.getParameterType())); + public boolean supportsParameter(MethodParameter param) { + return checkAnnotatedParamNoReactiveWrapper(param, RequestHeader.class, this::allParams); } + private boolean allParams(RequestHeader annotation, Class type) { + return Map.class.isAssignableFrom(type); + } + + @Override public Optional resolveArgumentValue(MethodParameter methodParameter, BindingContext context, ServerWebExchange exchange) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolver.java index 5db034549d..5912ae39ec 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolver.java @@ -22,6 +22,7 @@ import java.util.Optional; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.convert.ConversionService; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.server.ServerWebExchange; @@ -49,16 +50,22 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueSyncA * @param beanFactory a bean factory to use for resolving ${...} * placeholder and #{...} SpEL expressions in default values; * or {@code null} if default values are not expected to have expressions + * @param adapterRegistry for checking reactive type wrappers */ - public RequestHeaderMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { - super(beanFactory); + public RequestHeaderMethodArgumentResolver(ConfigurableBeanFactory beanFactory, + ReactiveAdapterRegistry adapterRegistry) { + + super(beanFactory, adapterRegistry); } @Override - public boolean supportsParameter(MethodParameter parameter) { - return (parameter.hasParameterAnnotation(RequestHeader.class) && - !Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())); + public boolean supportsParameter(MethodParameter param) { + return checkAnnotatedParamNoReactiveWrapper(param, RequestHeader.class, this::singleParam); + } + + private boolean singleParam(RequestHeader annotation, Class type) { + return !Map.class.isAssignableFrom(type); } @Override diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java index 8df4a84c2e..d35a825673 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java @@ -294,26 +294,26 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application List resolvers = new ArrayList<>(); // Annotation-based argument resolution - resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory())); - resolvers.add(new RequestParamMapMethodArgumentResolver()); - resolvers.add(new PathVariableMethodArgumentResolver(getBeanFactory())); - resolvers.add(new PathVariableMapMethodArgumentResolver()); + resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry(), false)); + resolvers.add(new RequestParamMapMethodArgumentResolver(getReactiveAdapterRegistry())); + resolvers.add(new PathVariableMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); + resolvers.add(new PathVariableMapMethodArgumentResolver(getReactiveAdapterRegistry())); resolvers.add(new RequestBodyArgumentResolver(getMessageReaders(), getReactiveAdapterRegistry())); - resolvers.add(new ModelAttributeMethodArgumentResolver(getReactiveAdapterRegistry())); - resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); - resolvers.add(new RequestHeaderMapMethodArgumentResolver()); - resolvers.add(new CookieValueMethodArgumentResolver(getBeanFactory())); - resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); - resolvers.add(new SessionAttributeMethodArgumentResolver(getBeanFactory())); - resolvers.add(new RequestAttributeMethodArgumentResolver(getBeanFactory())); + resolvers.add(new ModelAttributeMethodArgumentResolver(getReactiveAdapterRegistry(), false)); + resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); + resolvers.add(new RequestHeaderMapMethodArgumentResolver(getReactiveAdapterRegistry())); + resolvers.add(new CookieValueMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); + resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); + resolvers.add(new SessionAttributeMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); + resolvers.add(new RequestAttributeMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); // Type-based argument resolution resolvers.add(new HttpEntityArgumentResolver(getMessageReaders(), getReactiveAdapterRegistry())); - resolvers.add(new ModelArgumentResolver()); + resolvers.add(new ModelArgumentResolver(getReactiveAdapterRegistry())); resolvers.add(new ErrorsMethodArgumentResolver(getReactiveAdapterRegistry())); - resolvers.add(new ServerWebExchangeArgumentResolver()); - resolvers.add(new PrincipalArgumentResolver()); - resolvers.add(new WebSessionArgumentResolver()); + resolvers.add(new ServerWebExchangeArgumentResolver(getReactiveAdapterRegistry())); + resolvers.add(new PrincipalArgumentResolver(getReactiveAdapterRegistry())); + resolvers.add(new WebSessionArgumentResolver(getReactiveAdapterRegistry())); // Custom resolvers if (getCustomArgumentResolvers() != null) { @@ -321,7 +321,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application } // Catch-all - resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); + resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry(), true)); resolvers.add(new ModelAttributeMethodArgumentResolver(getReactiveAdapterRegistry(), true)); return resolvers; } @@ -330,19 +330,19 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application List resolvers = new ArrayList<>(); // Annotation-based argument resolution - resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); - resolvers.add(new RequestParamMapMethodArgumentResolver()); - resolvers.add(new PathVariableMethodArgumentResolver(getBeanFactory())); - resolvers.add(new PathVariableMapMethodArgumentResolver()); - resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); - resolvers.add(new RequestHeaderMapMethodArgumentResolver()); - resolvers.add(new CookieValueMethodArgumentResolver(getBeanFactory())); - resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); - resolvers.add(new RequestAttributeMethodArgumentResolver(getBeanFactory())); + resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry(), false)); + resolvers.add(new RequestParamMapMethodArgumentResolver(getReactiveAdapterRegistry())); + resolvers.add(new PathVariableMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); + resolvers.add(new PathVariableMapMethodArgumentResolver(getReactiveAdapterRegistry())); + resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); + resolvers.add(new RequestHeaderMapMethodArgumentResolver(getReactiveAdapterRegistry())); + resolvers.add(new CookieValueMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); + resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); + resolvers.add(new RequestAttributeMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry())); // Type-based argument resolution - resolvers.add(new ModelArgumentResolver()); - resolvers.add(new ServerWebExchangeArgumentResolver()); + resolvers.add(new ModelArgumentResolver(getReactiveAdapterRegistry())); + resolvers.add(new ServerWebExchangeArgumentResolver(getReactiveAdapterRegistry())); // Custom resolvers if (getCustomInitBinderArgumentResolvers() != null) { @@ -350,7 +350,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Application } // Catch-all - resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); + resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), getReactiveAdapterRegistry(), true)); return resolvers; } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java index 0bbafa80a0..3cc13359f4 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolver.java @@ -20,11 +20,13 @@ import java.util.Map; import java.util.Optional; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; 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.reactive.BindingContext; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver; import org.springframework.web.server.ServerWebExchange; @@ -43,20 +45,25 @@ import org.springframework.web.server.ServerWebExchange; * @since 5.0 * @see RequestParamMethodArgumentResolver */ -public class RequestParamMapMethodArgumentResolver implements SyncHandlerMethodArgumentResolver { +public class RequestParamMapMethodArgumentResolver extends HandlerMethodArgumentResolverSupport + implements SyncHandlerMethodArgumentResolver { + + + public RequestParamMapMethodArgumentResolver(ReactiveAdapterRegistry adapterRegistry) { + super(adapterRegistry); + } @Override - public boolean supportsParameter(MethodParameter methodParam) { - RequestParam requestParam = methodParam.getParameterAnnotation(RequestParam.class); - if (requestParam != null) { - if (Map.class.isAssignableFrom(methodParam.getParameterType())) { - return !StringUtils.hasText(requestParam.name()); - } - } - return false; + public boolean supportsParameter(MethodParameter param) { + return checkAnnotatedParamNoReactiveWrapper(param, RequestParam.class, this::allParams); } + private boolean allParams(RequestParam requestParam, Class type) { + return Map.class.isAssignableFrom(type) && !StringUtils.hasText(requestParam.name()); + } + + @Override public Optional resolveArgumentValue(MethodParameter methodParameter, BindingContext context, ServerWebExchange exchange) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolver.java index e4c2f2937f..193cb5436c 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolver.java @@ -23,6 +23,7 @@ import java.util.Optional; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.convert.converter.Converter; import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; @@ -56,46 +57,38 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueSyncAr private final boolean useDefaultResolution; - /** - * Class constructor. - * @param beanFactory a bean factory used for resolving ${...} placeholder - * and #{...} SpEL expressions in default values, or {@code null} if default - * values are not expected to contain expressions - */ - public RequestParamMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { - this(beanFactory, false); - } - /** * Class constructor with a default resolution mode flag. * @param beanFactory a bean factory used for resolving ${...} placeholder * and #{...} SpEL expressions in default values, or {@code null} if default * values are not expected to contain expressions + * @param adapterRegistry for checking reactive type wrappers * @param useDefaultResolution in default resolution mode a method argument * that is a simple type, as defined in {@link BeanUtils#isSimpleProperty}, * is treated as a request parameter even if it isn't annotated, the * request parameter name is derived from the method parameter name. */ public RequestParamMethodArgumentResolver(ConfigurableBeanFactory beanFactory, - boolean useDefaultResolution) { + ReactiveAdapterRegistry adapterRegistry, boolean useDefaultResolution) { - super(beanFactory); + super(beanFactory, adapterRegistry); this.useDefaultResolution = useDefaultResolution; } @Override - public boolean supportsParameter(MethodParameter parameter) { - if (parameter.hasParameterAnnotation(RequestParam.class)) { - if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { - String paramName = parameter.getParameterAnnotation(RequestParam.class).name(); - return StringUtils.hasText(paramName); - } - else { - return true; - } + public boolean supportsParameter(MethodParameter param) { + if (checkAnnotatedParamNoReactiveWrapper(param, RequestParam.class, this::singleParam)) { + return true; + } + else if (this.useDefaultResolution) { + return checkParamTypeNoReactiveWrapper(param, BeanUtils::isSimpleProperty); } - return (this.useDefaultResolution && BeanUtils.isSimpleProperty(parameter.getNestedParameterType())); + return false; + } + + private boolean singleParam(RequestParam requestParam, Class type) { + return !Map.class.isAssignableFrom(type) || StringUtils.hasText(requestParam.name()); } @Override @@ -132,11 +125,11 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueSyncAr private static class RequestParamNamedValueInfo extends NamedValueInfo { - public RequestParamNamedValueInfo() { + RequestParamNamedValueInfo() { super("", false, ValueConstants.DEFAULT_NONE); } - public RequestParamNamedValueInfo(RequestParam annotation) { + RequestParamNamedValueInfo(RequestParam annotation) { super(annotation.name(), annotation.required(), annotation.defaultValue()); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java index 342af7d236..6a095a2452 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolver.java @@ -19,10 +19,12 @@ package org.springframework.web.reactive.result.method.annotation; import java.util.Optional; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.http.HttpMethod; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.reactive.BindingContext; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver; import org.springframework.web.server.ServerWebExchange; @@ -43,16 +45,22 @@ import org.springframework.web.server.ServerWebExchange; * @see WebSessionArgumentResolver * @see PrincipalArgumentResolver */ -public class ServerWebExchangeArgumentResolver implements SyncHandlerMethodArgumentResolver { +public class ServerWebExchangeArgumentResolver extends HandlerMethodArgumentResolverSupport + implements SyncHandlerMethodArgumentResolver { + + + public ServerWebExchangeArgumentResolver(ReactiveAdapterRegistry adapterRegistry) { + super(adapterRegistry); + } @Override public boolean supportsParameter(MethodParameter parameter) { - Class paramType = parameter.getParameterType(); - return (ServerWebExchange.class.isAssignableFrom(paramType) || - ServerHttpRequest.class.isAssignableFrom(paramType) || - ServerHttpResponse.class.isAssignableFrom(paramType) || - HttpMethod.class == paramType); + return checkParamTypeNoReactiveWrapper(parameter, + type -> ServerWebExchange.class.isAssignableFrom(type) || + ServerHttpRequest.class.isAssignableFrom(type) || + ServerHttpResponse.class.isAssignableFrom(type) || + HttpMethod.class == type); } @Override diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/SessionAttributeMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/SessionAttributeMethodArgumentResolver.java index 27e5881865..bb557c1b3b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/SessionAttributeMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/SessionAttributeMethodArgumentResolver.java @@ -21,6 +21,7 @@ import reactor.core.publisher.Mono; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.web.bind.annotation.SessionAttribute; import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.server.ServerWebExchange; @@ -36,8 +37,10 @@ import org.springframework.web.server.ServerWebInputException; public class SessionAttributeMethodArgumentResolver extends AbstractNamedValueArgumentResolver { - public SessionAttributeMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { - super(beanFactory); + public SessionAttributeMethodArgumentResolver(ConfigurableBeanFactory beanFactory, + ReactiveAdapterRegistry adapterRegistry) { + + super(beanFactory, adapterRegistry); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/WebSessionArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/WebSessionArgumentResolver.java index bcac9a23dc..265a64a804 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/WebSessionArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/WebSessionArgumentResolver.java @@ -19,8 +19,11 @@ package org.springframework.web.reactive.result.method.annotation; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; +import org.springframework.util.Assert; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebSession; @@ -31,26 +34,26 @@ import org.springframework.web.server.WebSession; * @since 5.0 * @see ServerWebExchangeArgumentResolver */ -public class WebSessionArgumentResolver implements HandlerMethodArgumentResolver { +public class WebSessionArgumentResolver extends HandlerMethodArgumentResolverSupport + implements HandlerMethodArgumentResolver { + + + public WebSessionArgumentResolver(ReactiveAdapterRegistry adapterRegistry) { + super(adapterRegistry); + } + @Override public boolean supportsParameter(MethodParameter parameter) { - return (WebSession.class.isAssignableFrom(parameter.getParameterType())); + return checkParamTypeNoReactiveWrapper(parameter, WebSession.class::isAssignableFrom); } @Override public Mono resolveArgument(MethodParameter parameter, BindingContext context, ServerWebExchange exchange) { - Class paramType = parameter.getParameterType(); - if (WebSession.class.isAssignableFrom(paramType)) { - return exchange.getSession().cast(Object.class); - } - else { - // should never happen... - throw new IllegalArgumentException( - "Unknown parameter type: " + paramType + " in method: " + parameter.getMethod()); - } + Assert.isAssignable(WebSession.class, parameter.getParameterType()); + return exchange.getSession().cast(Object.class); } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/CookieValueMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/CookieValueMethodArgumentResolverTests.java index 797b5b8f57..ef32654a96 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/CookieValueMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/CookieValueMethodArgumentResolverTests.java @@ -25,11 +25,13 @@ import reactor.test.StepVerifier; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.http.HttpCookie; 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.ReflectionUtils; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.reactive.BindingContext; import org.springframework.web.server.ServerWebInputException; @@ -53,6 +55,7 @@ public class CookieValueMethodArgumentResolverTests { private MethodParameter cookieParameter; private MethodParameter cookieStringParameter; private MethodParameter stringParameter; + private MethodParameter cookieMonoParameter; @Before @@ -60,14 +63,16 @@ public class CookieValueMethodArgumentResolverTests { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.refresh(); - this.resolver = new CookieValueMethodArgumentResolver(context.getBeanFactory()); + ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry(); + this.resolver = new CookieValueMethodArgumentResolver(context.getBeanFactory(), adapterRegistry); this.request = MockServerHttpRequest.get("/").build(); this.bindingContext = new BindingContext(); - Method method = getClass().getMethod("params", HttpCookie.class, String.class, String.class); + Method method = ReflectionUtils.findMethod(getClass(), "params", (Class[]) null); this.cookieParameter = new SynthesizingMethodParameter(method, 0); this.cookieStringParameter = new SynthesizingMethodParameter(method, 1); this.stringParameter = new SynthesizingMethodParameter(method, 2); + this.cookieMonoParameter = new SynthesizingMethodParameter(method, 3); } @@ -75,7 +80,20 @@ public class CookieValueMethodArgumentResolverTests { public void supportsParameter() { assertTrue(this.resolver.supportsParameter(this.cookieParameter)); assertTrue(this.resolver.supportsParameter(this.cookieStringParameter)); + } + + @Test + public void doesNotSupportParameter() { assertFalse(this.resolver.supportsParameter(this.stringParameter)); + try { + this.resolver.supportsParameter(this.cookieMonoParameter); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "CookieValueMethodArgumentResolver doesn't support reactive type wrapper")); + } } @Test @@ -128,7 +146,8 @@ public class CookieValueMethodArgumentResolverTests { public void params( @CookieValue("name") HttpCookie cookie, @CookieValue(name = "name", defaultValue = "bar") String cookieString, - String stringParam) { + String stringParam, + @CookieValue Mono monoCookie) { } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ErrorsArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ErrorsArgumentResolverTests.java index e776a04270..28afeb1e6b 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ErrorsArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ErrorsArgumentResolverTests.java @@ -38,6 +38,7 @@ import org.springframework.web.server.adapter.DefaultServerWebExchange; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * Unit tests for {@link ErrorsMethodArgumentResolver}. @@ -78,12 +79,24 @@ public class ErrorsArgumentResolverTests { parameter = this.testMethod.arg(BindingResult.class); assertTrue(this.resolver.supportsParameter(parameter)); + } - parameter = this.testMethod.arg(ResolvableType.forClassWithGenerics(Mono.class, Errors.class)); - assertFalse(this.resolver.supportsParameter(parameter)); + @Test + public void doesNotSupport() throws Exception { - parameter = this.testMethod.arg(String.class); + MethodParameter parameter = this.testMethod.arg(String.class); assertFalse(this.resolver.supportsParameter(parameter)); + + try { + parameter = this.testMethod.arg(ResolvableType.forClassWithGenerics(Mono.class, Errors.class)); + assertFalse(this.resolver.supportsParameter(parameter)); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "ErrorsMethodArgumentResolver doesn't support reactive type wrapper")); + } } @Test diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ExpressionValueMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ExpressionValueMethodArgumentResolverTests.java index 1357e8ed42..0384e9a64b 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ExpressionValueMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ExpressionValueMethodArgumentResolverTests.java @@ -25,9 +25,11 @@ import reactor.core.publisher.Mono; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; 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.ReflectionUtils; import org.springframework.web.reactive.BindingContext; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.adapter.DefaultServerWebExchange; @@ -47,27 +49,43 @@ public class ExpressionValueMethodArgumentResolverTests { private MethodParameter paramSystemProperty; private MethodParameter paramNotSupported; + private MethodParameter paramAlsoNotSupported; @Before public void setup() throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.refresh(); - this.resolver = new ExpressionValueMethodArgumentResolver(context.getBeanFactory()); + ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry(); + this.resolver = new ExpressionValueMethodArgumentResolver(context.getBeanFactory(), adapterRegistry); ServerHttpRequest request = MockServerHttpRequest.get("/").build(); this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse()); - Method method = getClass().getMethod("params", int.class, String.class); + Method method = ReflectionUtils.findMethod(getClass(), "params", (Class[]) null); this.paramSystemProperty = new MethodParameter(method, 0); this.paramNotSupported = new MethodParameter(method, 1); + this.paramAlsoNotSupported = new MethodParameter(method, 2); } @Test public void supportsParameter() throws Exception { assertTrue(this.resolver.supportsParameter(this.paramSystemProperty)); + } + + @Test + public void doesNotSupport() throws Exception { assertFalse(this.resolver.supportsParameter(this.paramNotSupported)); + try { + this.resolver.supportsParameter(this.paramAlsoNotSupported); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "ExpressionValueMethodArgumentResolver doesn't support reactive type wrapper")); + } } @Test @@ -90,7 +108,10 @@ public class ExpressionValueMethodArgumentResolverTests { @SuppressWarnings("unused") - public void params(@Value("#{systemProperties.systemProperty}") int param1, String notSupported) { + public void params( + @Value("#{systemProperties.systemProperty}") int param1, + String notSupported, + @Value("#{systemProperties.foo}") Mono alsoNotSupported) { } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java index 45f9dac671..230e160738 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/HttpEntityArgumentResolverTests.java @@ -34,6 +34,7 @@ import rx.RxReactiveStreams; import rx.Single; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; import org.springframework.core.codec.StringDecoder; import org.springframework.http.HttpEntity; @@ -44,14 +45,19 @@ import org.springframework.http.codec.HttpMessageReader; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; import org.springframework.util.ObjectUtils; -import org.springframework.web.reactive.BindingContext; import org.springframework.web.method.ResolvableMethod; +import org.springframework.web.reactive.BindingContext; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; import org.springframework.web.server.adapter.DefaultServerWebExchange; -import static org.junit.Assert.*; -import static org.springframework.core.ResolvableType.*; +import static org.junit.Assert.assertEquals; +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.junit.Assert.fail; +import static org.springframework.core.ResolvableType.forClassWithGenerics; /** * Unit tests for {@link HttpEntityArgumentResolver}.When adding a test also @@ -78,7 +84,7 @@ public class HttpEntityArgumentResolverTests { private HttpEntityArgumentResolver createResolver() { List> readers = new ArrayList<>(); readers.add(new DecoderHttpMessageReader<>(new StringDecoder())); - return new HttpEntityArgumentResolver(readers); + return new HttpEntityArgumentResolver(readers, new ReactiveAdapterRegistry()); } @@ -105,6 +111,15 @@ public class HttpEntityArgumentResolverTests { public void doesNotSupport() throws Exception { assertFalse(this.resolver.supportsParameter(this.testMethod.arg(Mono.class, String.class))); assertFalse(this.resolver.supportsParameter(this.testMethod.arg(String.class))); + try { + this.resolver.supportsParameter(this.testMethod.arg(Mono.class, httpEntityType(String.class))); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "HttpEntityArgumentResolver doesn't support reactive type wrapper")); + } } @Test @@ -348,6 +363,7 @@ public class HttpEntityArgumentResolverTests { HttpEntity> rxJava2ObservableBody, HttpEntity> flowableBody, HttpEntity> completableFutureBody, - RequestEntity requestEntity) {} + RequestEntity requestEntity, + Mono> httpEntityMono) {} } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContextTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContextTests.java index 43ef83ab34..773444082e 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContextTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/InitBinderBindingContextTests.java @@ -25,6 +25,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.convert.ConversionService; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; @@ -112,7 +113,8 @@ public class InitBinderBindingContextTests { @Test public void createBinderTypeConversion() throws Exception { this.request = MockServerHttpRequest.get("/path?requestParam=22").build(); - this.argumentResolvers.add(new RequestParamMethodArgumentResolver(null, false)); + ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry(); + this.argumentResolvers.add(new RequestParamMethodArgumentResolver(null, adapterRegistry, false)); BindingContext context = createBindingContext("initBinderTypeConversion", WebDataBinder.class, int.class); WebDataBinder dataBinder = context.createDataBinder(createExchange(), null, "foo"); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolverTests.java index 4dbafbf74f..3e7ce99b39 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolverTests.java @@ -281,7 +281,7 @@ public class ModelAttributeMethodArgumentResolverTests { private ModelAttributeMethodArgumentResolver createResolver() { - return new ModelAttributeMethodArgumentResolver(new ReactiveAdapterRegistry()); + return new ModelAttributeMethodArgumentResolver(new ReactiveAdapterRegistry(), false); } private ServerWebExchange exchange(String formData) throws URISyntaxException { diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelInitializerTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelInitializerTests.java index 6e7ab164b1..740482049a 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelInitializerTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelInitializerTests.java @@ -16,6 +16,7 @@ package org.springframework.web.reactive.result.method.annotation; +import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.Map; @@ -120,14 +121,17 @@ public class ModelInitializerTests { private List getAttributeMethods(Object controller) { return MethodIntrospector .selectMethods(controller.getClass(), ATTRIBUTE_METHODS).stream() - .map(method -> { - InvocableHandlerMethod invocable = new InvocableHandlerMethod(controller, method); - invocable.setArgumentResolvers(Collections.singletonList(new ModelArgumentResolver())); - return invocable; - }) + .map(method -> toInvocable(controller, method)) .collect(Collectors.toList()); } + private InvocableHandlerMethod toInvocable(Object controller, Method method) { + ModelArgumentResolver resolver = new ModelArgumentResolver(new ReactiveAdapterRegistry()); + InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(controller, method); + handlerMethod.setArgumentResolvers(Collections.singletonList(resolver)); + return handlerMethod; + } + @SuppressWarnings("unused") private static class TestController { diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolverTests.java index b5bf241d2b..53ea8de59b 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/PathVariableMapMethodArgumentResolverTests.java @@ -26,9 +26,11 @@ import org.junit.Test; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; 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.ReflectionUtils; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.HandlerMapping; @@ -38,6 +40,7 @@ import org.springframework.web.server.adapter.DefaultServerWebExchange; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * Unit tests for {@link PathVariableMapMethodArgumentResolver}. @@ -53,19 +56,21 @@ public class PathVariableMapMethodArgumentResolverTests { private MethodParameter paramMap; private MethodParameter paramNamedMap; private MethodParameter paramMapNoAnnot; + private MethodParameter paramMonoMap; @Before public void setup() throws Exception { - this.resolver = new PathVariableMapMethodArgumentResolver(); + this.resolver = new PathVariableMapMethodArgumentResolver(new ReactiveAdapterRegistry()); ServerHttpRequest request = MockServerHttpRequest.get("/").build(); this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse()); - Method method = getClass().getMethod("handle", Map.class, Map.class, Map.class); + Method method = ReflectionUtils.findMethod(getClass(), "handle", (Class[]) null); this.paramMap = new MethodParameter(method, 0); this.paramNamedMap = new MethodParameter(method, 1); this.paramMapNoAnnot = new MethodParameter(method, 2); + this.paramMonoMap = new MethodParameter(method, 3); } @@ -74,6 +79,15 @@ public class PathVariableMapMethodArgumentResolverTests { assertTrue(resolver.supportsParameter(paramMap)); assertFalse(resolver.supportsParameter(paramNamedMap)); assertFalse(resolver.supportsParameter(paramMapNoAnnot)); + try { + this.resolver.supportsParameter(this.paramMonoMap); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "PathVariableMapMethodArgumentResolver doesn't support reactive type wrapper")); + } } @Test @@ -102,7 +116,8 @@ public class PathVariableMapMethodArgumentResolverTests { public void handle( @PathVariable Map map, @PathVariable(value = "name") Map namedMap, - Map mapWithoutAnnotat) { + Map mapWithoutAnnotat, + @PathVariable Mono> monoMap) { } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/PathVariableMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/PathVariableMethodArgumentResolverTests.java index f308bf3e0e..0c3e199407 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/PathVariableMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/PathVariableMethodArgumentResolverTests.java @@ -27,6 +27,7 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -44,6 +45,7 @@ import org.springframework.web.server.adapter.DefaultServerWebExchange; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * Unit tests for {@link PathVariableMethodArgumentResolver}. @@ -58,17 +60,15 @@ public class PathVariableMethodArgumentResolverTests { private ServerWebExchange exchange; private MethodParameter paramNamedString; - private MethodParameter paramString; - private MethodParameter paramNotRequired; - private MethodParameter paramOptional; + private MethodParameter paramMono; @Before public void setup() throws Exception { - this.resolver = new PathVariableMethodArgumentResolver(null); + this.resolver = new PathVariableMethodArgumentResolver(null, new ReactiveAdapterRegistry()); ServerHttpRequest request = MockServerHttpRequest.get("/").build(); this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse()); @@ -78,6 +78,7 @@ public class PathVariableMethodArgumentResolverTests { paramString = new SynthesizingMethodParameter(method, 1); paramNotRequired = new SynthesizingMethodParameter(method, 2); paramOptional = new SynthesizingMethodParameter(method, 3); + paramMono = new SynthesizingMethodParameter(method, 4); } @@ -85,6 +86,15 @@ public class PathVariableMethodArgumentResolverTests { public void supportsParameter() { assertTrue(this.resolver.supportsParameter(this.paramNamedString)); assertFalse(this.resolver.supportsParameter(this.paramString)); + try { + this.resolver.supportsParameter(this.paramMono); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "PathVariableMethodArgumentResolver doesn't support reactive type wrapper")); + } } @Test @@ -161,10 +171,13 @@ public class PathVariableMethodArgumentResolverTests { } - @SuppressWarnings("unused") - public void handle(@PathVariable(value = "name") String param1, String param2, + @SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"}) + public void handle( + @PathVariable(value = "name") String param1, + String param2, @PathVariable(name = "name", required = false) String param3, - @PathVariable("name") Optional param4) { + @PathVariable("name") Optional param4, + @PathVariable Mono param5) { } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestAttributeMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestAttributeMethodArgumentResolverTests.java index e9acc622cb..a2ca3227c2 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestAttributeMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestAttributeMethodArgumentResolverTests.java @@ -28,6 +28,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -61,7 +62,8 @@ public class RequestAttributeMethodArgumentResolverTests { public void setup() throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.refresh(); - this.resolver = new RequestAttributeMethodArgumentResolver(context.getBeanFactory()); + ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry(); + this.resolver = new RequestAttributeMethodArgumentResolver(context.getBeanFactory(), adapterRegistry); ServerHttpRequest request = MockServerHttpRequest.get("/").build(); this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse()); @@ -74,6 +76,15 @@ public class RequestAttributeMethodArgumentResolverTests { public void supportsParameter() throws Exception { assertTrue(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 0))); assertFalse(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 4))); + try { + this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 5)); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "RequestAttributeMethodArgumentResolver doesn't support reactive type wrapper")); + } } @Test @@ -151,7 +162,8 @@ public class RequestAttributeMethodArgumentResolverTests { @RequestAttribute("specialFoo") Foo namedFoo, @RequestAttribute(name="foo", required = false) Foo notRequiredFoo, @RequestAttribute(name="foo") Optional optionalFoo, - String notSupported) { + String notSupported, + @RequestAttribute Mono alsoNotSupported) { } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java index 4e252c6b83..87c1a03356 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestBodyArgumentResolverTests.java @@ -32,6 +32,7 @@ import rx.RxReactiveStreams; import rx.Single; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.codec.StringDecoder; import org.springframework.http.codec.DecoderHttpMessageReader; import org.springframework.http.codec.HttpMessageReader; @@ -69,7 +70,7 @@ public class RequestBodyArgumentResolverTests { public void setup() { List> readers = new ArrayList<>(); readers.add(new DecoderHttpMessageReader<>(new StringDecoder())); - this.resolver = new RequestBodyArgumentResolver(readers); + this.resolver = new RequestBodyArgumentResolver(readers, new ReactiveAdapterRegistry()); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolverTests.java index f0fb9b771e..4f6d8c6df6 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMapMethodArgumentResolverTests.java @@ -25,6 +25,7 @@ import org.junit.Test; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -32,6 +33,7 @@ import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.server.adapter.DefaultServerWebExchange; @@ -52,18 +54,21 @@ public class RequestHeaderMapMethodArgumentResolverTests { private MethodParameter paramMultiValueMap; private MethodParameter paramHttpHeaders; private MethodParameter paramUnsupported; + private MethodParameter paramAlsoUnsupported; @Before public void setup() throws Exception { - resolver = new RequestHeaderMapMethodArgumentResolver(); + resolver = new RequestHeaderMapMethodArgumentResolver(new ReactiveAdapterRegistry()); request = MockServerHttpRequest.get("/").build(); - Method method = getClass().getMethod("params", Map.class, MultiValueMap.class, HttpHeaders.class, Map.class); + Method method = ReflectionUtils.findMethod(getClass(), "params", (Class[]) null); paramMap = new SynthesizingMethodParameter(method, 0); paramMultiValueMap = new SynthesizingMethodParameter(method, 1); paramHttpHeaders = new SynthesizingMethodParameter(method, 2); paramUnsupported = new SynthesizingMethodParameter(method, 3); + paramUnsupported = new SynthesizingMethodParameter(method, 3); + paramAlsoUnsupported = new SynthesizingMethodParameter(method, 4); } @@ -73,6 +78,15 @@ public class RequestHeaderMapMethodArgumentResolverTests { assertTrue("MultiValueMap parameter not supported", resolver.supportsParameter(paramMultiValueMap)); assertTrue("HttpHeaders parameter not supported", resolver.supportsParameter(paramHttpHeaders)); assertFalse("non-@RequestParam map supported", resolver.supportsParameter(paramUnsupported)); + try { + this.resolver.supportsParameter(this.paramAlsoUnsupported); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "RequestHeaderMapMethodArgumentResolver doesn't support reactive type wrapper")); + } } @Test @@ -132,10 +146,12 @@ public class RequestHeaderMapMethodArgumentResolverTests { @SuppressWarnings("unused") - public void params(@RequestHeader Map param1, - @RequestHeader MultiValueMap param2, - @RequestHeader HttpHeaders param3, - Map unsupported) { + public void params( + @RequestHeader Map param1, + @RequestHeader MultiValueMap param2, + @RequestHeader HttpHeaders param3, + Map unsupported, + @RequestHeader Mono> alsoUnsupported) { } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolverTests.java index e4d2215fa3..f2d8fabbd0 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestHeaderMethodArgumentResolverTests.java @@ -29,6 +29,7 @@ import reactor.test.StepVerifier; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -64,13 +65,15 @@ public class RequestHeaderMethodArgumentResolverTests { private MethodParameter paramNamedValueMap; private MethodParameter paramDate; private MethodParameter paramInstant; + private MethodParameter paramMono; @Before public void setup() throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.refresh(); - this.resolver = new RequestHeaderMethodArgumentResolver(context.getBeanFactory()); + ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry(); + this.resolver = new RequestHeaderMethodArgumentResolver(context.getBeanFactory(), adapterRegistry); this.request = MockServerHttpRequest.get("/").build(); @@ -87,6 +90,7 @@ public class RequestHeaderMethodArgumentResolverTests { this.paramNamedValueMap = new SynthesizingMethodParameter(method, 5); this.paramDate = new SynthesizingMethodParameter(method, 6); this.paramInstant = new SynthesizingMethodParameter(method, 7); + this.paramMono = new SynthesizingMethodParameter(method, 8); } @@ -95,6 +99,15 @@ public class RequestHeaderMethodArgumentResolverTests { assertTrue("String parameter not supported", resolver.supportsParameter(paramNamedDefaultValueStringHeader)); assertTrue("String array parameter not supported", resolver.supportsParameter(paramNamedValueStringArray)); assertFalse("non-@RequestParam parameter supported", resolver.supportsParameter(paramNamedValueMap)); + try { + this.resolver.supportsParameter(this.paramMono); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "RequestHeaderMethodArgumentResolver doesn't support reactive type wrapper")); + } } @Test @@ -237,7 +250,8 @@ public class RequestHeaderMethodArgumentResolverTests { @RequestHeader("${systemProperty}") String param5, @RequestHeader("name") Map unsupported, @RequestHeader("name") Date dateParam, - @RequestHeader("name") Instant instantParam) { + @RequestHeader("name") Instant instantParam, + @RequestHeader Mono alsoNotSupported) { } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolverTests.java index db56747e1c..d06a9fc9c6 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMapMethodArgumentResolverTests.java @@ -23,8 +23,10 @@ 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.ReactiveAdapterRegistry; import org.springframework.http.MediaType; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; @@ -37,6 +39,7 @@ import org.springframework.web.server.adapter.DefaultServerWebExchange; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.springframework.web.method.MvcAnnotationPredicates.requestParam; /** @@ -52,7 +55,7 @@ public class RequestParamMapMethodArgumentResolverTests { @Before public void setup() throws Exception { - this.resolver = new RequestParamMapMethodArgumentResolver(); + this.resolver = new RequestParamMapMethodArgumentResolver(new ReactiveAdapterRegistry()); } @@ -69,6 +72,17 @@ public class RequestParamMapMethodArgumentResolverTests { param = this.testMethod.annotNotPresent(RequestParam.class).arg(Map.class); assertFalse(this.resolver.supportsParameter(param)); + + try { + param = this.testMethod.annot(requestParam()).arg(Mono.class, Map.class); + this.resolver.supportsParameter(param); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "RequestParamMapMethodArgumentResolver doesn't support reactive type wrapper")); + } } @Test @@ -120,7 +134,8 @@ public class RequestParamMapMethodArgumentResolverTests { @RequestParam Map param1, @RequestParam MultiValueMap param2, @RequestParam("name") Map param3, - Map param4) { + Map param4, + @RequestParam Mono> paramMono) { } } \ No newline at end of file diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverTests.java index 3d614b9507..5fb661dd05 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverTests.java @@ -17,6 +17,7 @@ package org.springframework.web.reactive.result.method.annotation; import java.net.URISyntaxException; +import java.time.Duration; import java.util.Map; import java.util.Optional; @@ -26,12 +27,14 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.http.MediaType; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; +import org.springframework.web.method.MvcAnnotationPredicates; import org.springframework.web.method.ResolvableMethod; import org.springframework.web.reactive.BindingContext; import org.springframework.web.server.ServerWebExchange; @@ -43,6 +46,7 @@ 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.junit.Assert.fail; import static org.springframework.core.ResolvableType.forClassWithGenerics; import static org.springframework.web.method.MvcAnnotationPredicates.requestParam; @@ -63,7 +67,8 @@ public class RequestParamMethodArgumentResolverTests { @Before public void setup() throws Exception { - this.resolver = new RequestParamMethodArgumentResolver(null, true); + ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry(); + this.resolver = new RequestParamMethodArgumentResolver(null, adapterRegistry, true); ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); initializer.setConversionService(new DefaultFormattingConversionService()); @@ -73,7 +78,6 @@ public class RequestParamMethodArgumentResolverTests { @Test public void supportsParameter() { - this.resolver = new RequestParamMethodArgumentResolver(null, true); MethodParameter param = this.testMethod.annot(requestParam().notRequired("bar")).arg(String.class); assertTrue(this.resolver.supportsParameter(param)); @@ -96,11 +100,42 @@ public class RequestParamMethodArgumentResolverTests { param = this.testMethod.annot(requestParam().notRequired()).arg(String.class); assertTrue(this.resolver.supportsParameter(param)); - param = this.testMethod.annotNotPresent(RequestParam.class).arg(String.class); - this.resolver = new RequestParamMethodArgumentResolver(null, false); + } + + @Test + public void doesNotSupportParameterWithDefaultResolutionTurnedOff() { + ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry(); + this.resolver = new RequestParamMethodArgumentResolver(null, adapterRegistry, false); + + MethodParameter param = this.testMethod.annotNotPresent(RequestParam.class).arg(String.class); assertFalse(this.resolver.supportsParameter(param)); } + @Test + public void doesNotSupportReactiveWrapper() { + MethodParameter param; + try { + param = this.testMethod.annot(requestParam()).arg(Mono.class, String.class); + this.resolver.supportsParameter(param); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "RequestParamMethodArgumentResolver doesn't support reactive type wrapper")); + } + try { + param = this.testMethod.annotNotPresent(RequestParam.class).arg(Mono.class, String.class); + this.resolver.supportsParameter(param); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "RequestParamMethodArgumentResolver doesn't support reactive type wrapper")); + } + } + @Test public void resolveWithQueryString() throws Exception { MethodParameter param = this.testMethod.annot(requestParam().notRequired("bar")).arg(String.class); @@ -208,7 +243,7 @@ public class RequestParamMethodArgumentResolverTests { } private Object resolve(MethodParameter parameter, ServerWebExchange exchange) { - return this.resolver.resolveArgument(parameter, this.bindContext, exchange).blockMillis(0); + return this.resolver.resolveArgument(parameter, this.bindContext, exchange).block(Duration.ZERO); } @@ -219,6 +254,7 @@ public class RequestParamMethodArgumentResolverTests { @RequestParam("name") Map param3, @RequestParam Map param4, String stringNotAnnot, + Mono monoStringNotAnnot, @RequestParam("name") String paramRequired, @RequestParam(name = "name", required = false) String paramNotRequired, @RequestParam("name") Optional paramOptional, diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolverTests.java index 7cd17a04a0..cf5685771d 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ServerWebExchangeArgumentResolverTests.java @@ -21,6 +21,7 @@ import org.junit.Test; import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.http.HttpMethod; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; @@ -43,7 +44,8 @@ import static org.mockito.Mockito.*; */ public class ServerWebExchangeArgumentResolverTests { - private final ServerWebExchangeArgumentResolver resolver = new ServerWebExchangeArgumentResolver(); + private final ServerWebExchangeArgumentResolver resolver = + new ServerWebExchangeArgumentResolver(new ReactiveAdapterRegistry()); private ServerWebExchange exchange; @@ -67,6 +69,15 @@ public class ServerWebExchangeArgumentResolverTests { assertTrue(this.resolver.supportsParameter(this.testMethod.arg(ServerHttpResponse.class))); assertTrue(this.resolver.supportsParameter(this.testMethod.arg(HttpMethod.class))); assertFalse(this.resolver.supportsParameter(this.testMethod.arg(String.class))); + try { + this.resolver.supportsParameter(this.testMethod.arg(Mono.class, ServerWebExchange.class)); + fail(); + } + catch (IllegalStateException ex) { + assertTrue("Unexpected error message:\n" + ex.getMessage(), + ex.getMessage().startsWith( + "ServerWebExchangeArgumentResolver doesn't support reactive type wrapper")); + } } @Test @@ -91,7 +102,8 @@ public class ServerWebExchangeArgumentResolverTests { ServerHttpResponse response, WebSession session, HttpMethod httpMethod, - String s) { + String s, + Mono monoExchange) { } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/SessionAttributeMethodArgumentResolverTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/SessionAttributeMethodArgumentResolverTests.java index f4c007e31c..e952d667ff 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/SessionAttributeMethodArgumentResolverTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/SessionAttributeMethodArgumentResolverTests.java @@ -28,6 +28,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; +import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -44,8 +45,14 @@ 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.mockito.Mockito.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Unit tests for {@link SessionAttributeMethodArgumentResolver}. @@ -67,7 +74,8 @@ public class SessionAttributeMethodArgumentResolverTests { public void setup() throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.refresh(); - this.resolver = new SessionAttributeMethodArgumentResolver(context.getBeanFactory()); + ReactiveAdapterRegistry adapterRegistry = new ReactiveAdapterRegistry(); + this.resolver = new SessionAttributeMethodArgumentResolver(context.getBeanFactory(), adapterRegistry); this.session = mock(WebSession.class); WebSessionManager sessionManager = new MockWebSessionManager(this.session); diff --git a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverKotlinTests.kt b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverKotlinTests.kt index 4330414f99..9511dfe697 100644 --- a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverKotlinTests.kt +++ b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverKotlinTests.kt @@ -3,6 +3,7 @@ package org.springframework.web.reactive.result.method.annotation import org.junit.Before import org.junit.Test import org.springframework.core.MethodParameter +import org.springframework.core.ReactiveAdapterRegistry import org.springframework.core.annotation.SynthesizingMethodParameter import org.springframework.format.support.DefaultFormattingConversionService import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest @@ -34,7 +35,7 @@ class RequestParamMethodArgumentResolverKotlinTests { @Before fun setup() { - this.resolver = RequestParamMethodArgumentResolver(null, true) + this.resolver = RequestParamMethodArgumentResolver(null, ReactiveAdapterRegistry(), true) this.request = MockServerHttpRequest.get("/").build() val initializer = ConfigurableWebBindingInitializer() initializer.conversionService = DefaultFormattingConversionService()