提交 4d9126a4 编写于 作者: R Rossen Stoyanchev

Add support for more annotated method arguments

 - @PathVariable
 - @RequestHeader
 - @RequestParam
 - @CookieValue
 - @Value
 - @RequestAttribute
 - @SessionAttribute
上级 4e8c21e8
......@@ -27,7 +27,7 @@ repositories {
}
ext {
springVersion = '4.3.0.RC1'
springVersion = '4.3.0.BUILD-SNAPSHOT'
reactorVersion = '2.5.0.BUILD-SNAPSHOT'
reactorNettyVersion = '2.5.0.BUILD-SNAPSHOT'
tomcatVersion = '8.0.33'
......
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import reactor.core.publisher.Mono;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeMismatchException;
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.convert.ConversionService;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerErrorException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
/**
* Abstract base class for resolving method arguments from a named value.
* Request parameters, request headers, and path variables are examples of named
* values. Each may have a name, a required flag, and a default value.
* <p>Subclasses define how to do the following:
* <ul>
* <li>Obtain named value information for a method parameter
* <li>Resolve names into argument values
* <li>Handle missing argument values when argument values are required
* <li>Optionally handle a resolved value
* </ul>
* <p>A default value string can contain ${...} placeholders and Spring Expression
* Language #{...} expressions. For this to work a
* {@link ConfigurableBeanFactory} must be supplied to the class constructor.
*
* @author Rossen Stoyanchev
*/
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
private final ConfigurableBeanFactory configurableBeanFactory;
private final BeanExpressionContext expressionContext;
private final Map<MethodParameter, NamedValueInfo> namedValueInfoCache = new ConcurrentHashMap<>(256);
/** Instead of a WebDataBinder for now */
private final SimpleTypeConverter typeConverter;
/**
* @param conversionService for type conversion (to be replaced with WebDataBinder)
* @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
*/
public AbstractNamedValueMethodArgumentResolver(ConversionService conversionService,
ConfigurableBeanFactory beanFactory) {
Assert.notNull(conversionService, "'conversionService' is required.");
this.typeConverter = new SimpleTypeConverter();
this.typeConverter.setConversionService(conversionService);
this.configurableBeanFactory = beanFactory;
this.expressionContext = (beanFactory != null ? new BeanExpressionContext(beanFactory, null) : null);
}
@Override
public Mono<Object> resolveArgument(MethodParameter parameter, ModelMap model, ServerWebExchange exchange) {
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
return Mono.error(new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]"));
}
return resolveName(resolvedName.toString(), nestedParameter, exchange)
.map(arg -> {
if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
arg = applyConversion(arg, parameter);
handleResolvedValue(arg, namedValueInfo.name, parameter, model, exchange);
return arg;
})
.otherwiseIfEmpty(getDefaultValue(namedValueInfo, parameter, model, exchange));
}
/**
* Obtain the named value for the given method parameter.
*/
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
if (namedValueInfo == null) {
namedValueInfo = createNamedValueInfo(parameter);
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
this.namedValueInfoCache.put(parameter, namedValueInfo);
}
return namedValueInfo;
}
/**
* Create the {@link NamedValueInfo} object for the given method parameter.
* Implementations typically retrieve the method annotation by means of
* {@link MethodParameter#getParameterAnnotation(Class)}.
* @param parameter the method parameter
* @return the named value information
*/
protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter);
/**
* Create a new NamedValueInfo based on the given NamedValueInfo with
* sanitized values.
*/
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
String name = info.name;
if (info.name.length() == 0) {
name = parameter.getParameterName();
if (name == null) {
String type = parameter.getNestedParameterType().getName();
throw new IllegalArgumentException("Name for argument type [" + type + "] not " +
"available, and parameter name information not found in class file either.");
}
}
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
return new NamedValueInfo(name, info.required, defaultValue);
}
/**
* Resolve the given annotation-specified value,
* potentially containing placeholders and expressions.
*/
private Object resolveStringValue(String value) {
if (this.configurableBeanFactory == null) {
return value;
}
String placeholdersResolved = this.configurableBeanFactory.resolveEmbeddedValue(value);
BeanExpressionResolver exprResolver = this.configurableBeanFactory.getBeanExpressionResolver();
if (exprResolver == null) {
return value;
}
return exprResolver.evaluate(placeholdersResolved, this.expressionContext);
}
/**
* Resolve the given parameter type and value name into an argument value.
* @param name the name of the value being resolved
* @param parameter the method parameter to resolve to an argument value
* (pre-nested in case of a {@link java.util.Optional} declaration)
* @param exchange the current exchange
* @return the resolved argument (may be {@code null})
*/
protected abstract Mono<Object> resolveName(String name, MethodParameter parameter,
ServerWebExchange exchange);
private Object applyConversion(Object value, MethodParameter parameter) {
try {
value = this.typeConverter.convertIfNecessary(value, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new ServerErrorException("Conversion not supported.", parameter, ex);
}
catch (TypeMismatchException ex) {
throw new ServerWebInputException("Type mismatch.", parameter, ex);
}
return value;
}
private Mono<Object> getDefaultValue(NamedValueInfo namedValueInfo, MethodParameter parameter,
ModelMap model, ServerWebExchange exchange) {
Object value = null;
try {
if (namedValueInfo.defaultValue != null) {
value = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !parameter.isOptional()) {
handleMissingValue(namedValueInfo.name, parameter, exchange);
}
value = handleNullValue(namedValueInfo.name, value, parameter.getNestedParameterType());
value = applyConversion(value, parameter);
handleResolvedValue(value, namedValueInfo.name, parameter, model, exchange);
return Mono.justOrEmpty(value);
}
catch (Throwable ex) {
return Mono.error(ex);
}
}
/**
* Invoked when a named value is required, but
* {@link #resolveName(String, MethodParameter, ServerWebExchange)} returned
* {@code null} and there is no default value. Subclasses typically throw an
* exception in this case.
* @param name the name for the value
* @param parameter the method parameter
* @param exchange the current exchange
*/
@SuppressWarnings("UnusedParameters")
protected void handleMissingValue(String name, MethodParameter parameter, ServerWebExchange exchange) {
handleMissingValue(name, parameter);
}
/**
* Invoked when a named value is required, but
* {@link #resolveName(String, MethodParameter, ServerWebExchange)} returned
* {@code null} and there is no default value. Subclasses typically throw an
* exception in this case.
* @param name the name for the value
* @param parameter the method parameter
*/
protected void handleMissingValue(String name, MethodParameter parameter) {
String typeName = parameter.getNestedParameterType().getSimpleName();
throw new ServerWebInputException("Missing argument '" + name + "' for method " +
"parameter of type " + typeName, parameter);
}
/**
* A {@code null} results in a {@code false} value for {@code boolean}s or
* an exception for other primitives.
*/
private Object handleNullValue(String name, Object value, Class<?> paramType) {
if (value == null) {
if (Boolean.TYPE.equals(paramType)) {
return Boolean.FALSE;
}
else if (paramType.isPrimitive()) {
throw new IllegalStateException("Optional " + paramType.getSimpleName() +
" parameter '" + name + "' is present but cannot be translated into a" +
" null value due to being declared as a primitive type. " +
"Consider declaring it as object wrapper for the corresponding primitive type.");
}
}
return value;
}
/**
* Invoked after a value is resolved.
* @param arg the resolved argument value
* @param name the argument name
* @param parameter the argument parameter type
* @param model the model
* @param exchange the current exchange
*/
@SuppressWarnings("UnusedParameters")
protected void handleResolvedValue(Object arg, String name, MethodParameter parameter,
ModelMap model, ServerWebExchange exchange) {
}
/**
* Represents the information about a named value, including name, whether
* it's required and a default value.
*/
protected static class NamedValueInfo {
private final String name;
private final boolean required;
private final String defaultValue;
public NamedValueInfo(String name, boolean required, String defaultValue) {
this.name = name;
this.required = required;
this.defaultValue = defaultValue;
}
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.http.HttpCookie;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
/**
* Resolve method arguments annotated with {@code @CookieValue}.
*
* <p>An {@code @CookieValue} is a named value that is resolved from a cookie.
* It has a required flag and a default value to fall back on when the cookie
* does not exist.
*
* @author Rossen Stoyanchev
*/
public class CookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
/**
* @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
*/
public CookieValueMethodArgumentResolver(ConversionService conversionService,
ConfigurableBeanFactory beanFactory) {
super(conversionService, beanFactory);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(CookieValue.class);
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
CookieValue annotation = parameter.getParameterAnnotation(CookieValue.class);
return new CookieValueNamedValueInfo(annotation);
}
@Override
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
HttpCookie cookie = exchange.getRequest().getCookies().getFirst(name);
if (HttpCookie.class.isAssignableFrom(parameter.getNestedParameterType())) {
return Mono.justOrEmpty(cookie);
}
else if (cookie != null) {
return Mono.justOrEmpty(cookie.getValue());
}
else {
return Mono.empty();
}
}
@Override
protected void handleMissingValue(String name, MethodParameter parameter) {
String type = parameter.getNestedParameterType().getSimpleName();
String reason = "Missing cookie '" + name + "' for method parameter of type " + type;
throw new ServerWebInputException(reason, parameter);
}
private static class CookieValueNamedValueInfo extends NamedValueInfo {
private CookieValueNamedValueInfo(CookieValue annotation) {
super(annotation.name(), annotation.required(), annotation.defaultValue());
}
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.web.server.ServerWebExchange;
/**
* Resolves method arguments annotated with {@code @Value}.
*
* <p>An {@code @Value} does not have a name but gets resolved from the default
* value string, which may contain ${...} placeholder or Spring Expression
* Language #{...} expressions.
*
* @author Rossen Stoyanchev
*/
public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
/**
* @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
*/
public ExpressionValueMethodArgumentResolver(ConversionService conversionService,
ConfigurableBeanFactory beanFactory) {
super(conversionService, beanFactory);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(Value.class);
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
Value annotation = parameter.getParameterAnnotation(Value.class);
return new ExpressionValueNamedValueInfo(annotation);
}
@Override
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
// No name to resolve
return Mono.empty();
}
@Override
protected void handleMissingValue(String name, MethodParameter parameter) {
throw new UnsupportedOperationException("@Value is never required: " + parameter.getMethod());
}
private static class ExpressionValueNamedValueInfo extends NamedValueInfo {
private ExpressionValueNamedValueInfo(Value annotation) {
super("@Value", false, annotation.value());
}
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
/**
* Resolver for {@link Map} method arguments also annotated with
* {@link PathVariable @PathVariable} where the annotation does not specify a
* path variable name. The resulting {@link Map} argument is a coyp of all URI
* template name-value pairs.
*
* @author Rossen Stoyanchev
* @see PathVariableMethodArgumentResolver
*/
public class PathVariableMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
return (ann != null && (Map.class.isAssignableFrom(parameter.getParameterType()))
&& !StringUtils.hasText(ann.value()));
}
/**
* Return a Map with all URI template variables or an empty map.
*/
@Override
public Mono<Object> resolveArgument(MethodParameter parameter, ModelMap model,
ServerWebExchange exchange) {
String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
Optional<Object> value = exchange.getAttribute(name);
return (value.isPresent() ? Mono.just(value.get()) : Mono.just(Collections.emptyMap()));
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.util.Map;
import java.util.Optional;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.ServerErrorException;
import org.springframework.web.server.ServerWebExchange;
/**
* Resolves method arguments annotated with @{@link PathVariable}.
*
* <p>An @{@link PathVariable} is a named value that gets resolved from a URI
* template variable. It is always required and does not have a default value
* to fall back on. See the base class
* {@link org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver}
* for more information on how named values are processed.
*
* <p>If the method parameter type is {@link Map}, the name specified in the
* annotation is used to resolve the URI variable String value. The value is
* then converted to a {@link Map} via type conversion, assuming a suitable
* {@link Converter}.
*
* @author Rossen Stoyanchev
* @see PathVariableMapMethodArgumentResolver
*/
public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
public PathVariableMethodArgumentResolver(ConversionService conversionService,
ConfigurableBeanFactory beanFactory) {
super(conversionService, beanFactory);
}
@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;
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
PathVariable annotation = parameter.getParameterAnnotation(PathVariable.class);
return new PathVariableNamedValueInfo(annotation);
}
@Override
@SuppressWarnings("unchecked")
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
String attributeName = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
Optional<Object> optional = exchange.getAttribute(attributeName);
Object value = null;
if (optional.isPresent()) {
value = ((Map<String, String>) optional.get()).get(name);
}
return Mono.justOrEmpty(value);
}
@Override
protected void handleMissingValue(String name, MethodParameter parameter) {
throw new ServerErrorException(name, parameter);
}
@Override
@SuppressWarnings("unchecked")
protected void handleResolvedValue(Object arg, String name, MethodParameter parameter,
ModelMap model, ServerWebExchange exchange) {
// TODO: View.PATH_VARIABLES ?
}
private static class PathVariableNamedValueInfo extends NamedValueInfo {
public PathVariableNamedValueInfo(PathVariable annotation) {
super(annotation.value(), true, ValueConstants.DEFAULT_NONE);
}
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
/**
* Resolves method arguments annotated with an @{@link RequestAttribute}.
*
* @author Rossen Stoyanchev
* @see SessionAttributeMethodArgumentResolver
*/
public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
public RequestAttributeMethodArgumentResolver(ConversionService conversionService,
ConfigurableBeanFactory beanFactory) {
super(conversionService, beanFactory);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestAttribute.class);
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestAttribute annot = parameter.getParameterAnnotation(RequestAttribute.class);
return new NamedValueInfo(annot.name(), annot.required(), ValueConstants.DEFAULT_NONE);
}
@Override
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange){
return Mono.justOrEmpty(exchange.getAttribute(name));
}
@Override
protected void handleMissingValue(String name, MethodParameter parameter) {
String type = parameter.getNestedParameterType().getSimpleName();
String reason = "Missing request attribute '" + name + "' of type " + type;
throw new ServerWebInputException(reason, parameter);
}
}
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -16,37 +16,47 @@
package org.springframework.web.reactive.result.method.annotation;
import java.util.Map;
import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Support {@code @RequestParam} but for query params only.
* Resolves {@link Map} method arguments annotated with {@code @RequestHeader}.
* For individual header values annotated with {@code @RequestHeader} see
* {@link RequestHeaderMethodArgumentResolver} instead.
*
* <p>The created {@link Map} contains all request header name/value pairs.
* The method parameter type may be a {@link MultiValueMap} to receive all
* values for a header, not only the first one.
*
* @author Rossen Stoyanchev
* @see RequestHeaderMethodArgumentResolver
*/
public class RequestParamArgumentResolver implements HandlerMethodArgumentResolver {
public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestParam.class);
return (parameter.hasParameterAnnotation(RequestHeader.class) &&
Map.class.isAssignableFrom(parameter.getParameterType()));
}
@Override
public Mono<Object> resolveArgument(MethodParameter param, ModelMap model, ServerWebExchange exchange) {
RequestParam annotation = param.getParameterAnnotation(RequestParam.class);
String name = (annotation.value().length() != 0 ? annotation.value() : param.getParameterName());
UriComponents uriComponents = UriComponentsBuilder.fromUri(exchange.getRequest().getURI()).build();
return Mono.justOrEmpty(uriComponents.getQueryParams().getFirst(name));
public Mono<Object> resolveArgument(MethodParameter parameter, ModelMap model, ServerWebExchange exchange) {
HttpHeaders headers = exchange.getRequest().getHeaders();
if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
return Mono.just(headers);
}
else {
return Mono.just(headers.toSingleValueMap());
}
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
/**
* Resolves method arguments annotated with {@code @RequestHeader} except for
* {@link Map} arguments. See {@link RequestHeaderMapMethodArgumentResolver} for
* details on {@link Map} arguments annotated with {@code @RequestHeader}.
*
* <p>An {@code @RequestHeader} is a named value resolved from a request header.
* It has a required flag and a default value to fall back on when the request
* header does not exist.
*
* <p>A {@link ConversionService} is invoked to apply type conversion to resolved
* request header values that don't yet match the method parameter type.
*
* @author Rossen Stoyanchev
* @see RequestHeaderMapMethodArgumentResolver
*/
public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
/**
* @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
*/
public RequestHeaderMethodArgumentResolver(ConversionService conversionService,
ConfigurableBeanFactory beanFactory) {
super(conversionService, beanFactory);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(RequestHeader.class) &&
!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType()));
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestHeader annotation = parameter.getParameterAnnotation(RequestHeader.class);
return new RequestHeaderNamedValueInfo(annotation);
}
@Override
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
List<String> headerValues = exchange.getRequest().getHeaders().get(name);
Object result = null;
if (headerValues != null) {
result = (headerValues.size() == 1 ? headerValues.get(0) : headerValues);
}
return Mono.justOrEmpty(result);
}
@Override
protected void handleMissingValue(String name, MethodParameter parameter) {
String type = parameter.getNestedParameterType().getSimpleName();
throw new ServerWebInputException("Missing request header '" + name +
"' for method parameter of type " + type);
}
private static class RequestHeaderNamedValueInfo extends NamedValueInfo {
private RequestHeaderNamedValueInfo(RequestHeader annotation) {
super(annotation.name(), annotation.required(), annotation.defaultValue());
}
}
}
......@@ -28,7 +28,11 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.codec.support.ByteBufferDecoder;
import org.springframework.core.codec.support.ByteBufferEncoder;
import org.springframework.core.codec.support.JacksonJsonDecoder;
......@@ -57,7 +61,7 @@ import org.springframework.web.server.ServerWebExchange;
/**
* @author Rossen Stoyanchev
*/
public class RequestMappingHandlerAdapter implements HandlerAdapter, InitializingBean {
public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactoryAware, InitializingBean {
private static Log logger = LogFactory.getLog(RequestMappingHandlerAdapter.class);
......@@ -69,6 +73,9 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
new ConcurrentHashMap<>(64);
private ConfigurableBeanFactory beanFactory;
/**
* Configure the complete list of supported argument types thus overriding
......@@ -94,24 +101,52 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, Initializin
return this.conversionService;
}
/**
* A {@link ConfigurableBeanFactory} is expected for resolving expressions
* in method argument default values.
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableBeanFactory) {
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
}
}
public ConfigurableBeanFactory getBeanFactory() {
return beanFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
if (ObjectUtils.isEmpty(this.argumentResolvers)) {
List<HttpMessageConverter<?>> messageConverters = Arrays.asList(
new CodecHttpMessageConverter<ByteBuffer>(new ByteBufferEncoder(),
new ByteBufferDecoder()),
new CodecHttpMessageConverter<String>(new StringEncoder(),
new StringDecoder()),
new CodecHttpMessageConverter<Object>(new Jaxb2Encoder(),
new Jaxb2Decoder()),
List<HttpMessageConverter<?>> converters = Arrays.asList(
new CodecHttpMessageConverter<ByteBuffer>(new ByteBufferEncoder(), new ByteBufferDecoder()),
new CodecHttpMessageConverter<String>(new StringEncoder(), new StringDecoder()),
new CodecHttpMessageConverter<Object>(new Jaxb2Encoder(), new Jaxb2Decoder()),
new CodecHttpMessageConverter<Object>(new JacksonJsonEncoder(),
new JacksonJsonDecoder(new JsonObjectDecoder())));
this.argumentResolvers.add(new RequestParamArgumentResolver());
this.argumentResolvers.add(new RequestBodyArgumentResolver(messageConverters,
this.conversionService));
// Annotation-based argument resolution
ConversionService cs = getConversionService();
this.argumentResolvers.add(new RequestParamMethodArgumentResolver(cs, getBeanFactory(), false));
this.argumentResolvers.add(new RequestParamMapMethodArgumentResolver());
this.argumentResolvers.add(new PathVariableMethodArgumentResolver(cs, getBeanFactory()));
this.argumentResolvers.add(new PathVariableMapMethodArgumentResolver());
this.argumentResolvers.add(new RequestBodyArgumentResolver(converters, cs));
this.argumentResolvers.add(new RequestHeaderMethodArgumentResolver(cs, getBeanFactory()));
this.argumentResolvers.add(new RequestHeaderMapMethodArgumentResolver());
this.argumentResolvers.add(new CookieValueMethodArgumentResolver(cs, getBeanFactory()));
this.argumentResolvers.add(new ExpressionValueMethodArgumentResolver(cs, getBeanFactory()));
this.argumentResolvers.add(new SessionAttributeMethodArgumentResolver(cs, getBeanFactory()));
this.argumentResolvers.add(new RequestAttributeMethodArgumentResolver(cs , getBeanFactory()));
// Type-based argument resolution
this.argumentResolvers.add(new ModelArgumentResolver());
// Catch-all
this.argumentResolvers.add(new RequestParamMethodArgumentResolver(cs, getBeanFactory(), true));
}
}
......
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.util.Map;
import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter;
import org.springframework.ui.ModelMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
/**
* Resolver for {@link Map} method arguments annotated with
* {@link RequestParam @RequestParam} where the annotation does not specify a
* request parameter name. See {@link RequestParamMethodArgumentResolver} for
* resolving {@link Map} method arguments with a request parameter name.
*
* <p>The created {@link Map} contains all request parameter name-value pairs.
* If the method parameter type is {@link MultiValueMap} instead, the created
* map contains all request parameters and all there values for cases where
* request parameters have multiple values.
*
* @author Rossen Stoyanchev
* @see RequestParamMethodArgumentResolver
*/
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
if (requestParam != null) {
if (Map.class.isAssignableFrom(parameter.getParameterType())) {
return !StringUtils.hasText(requestParam.name());
}
}
return false;
}
@Override
public Mono<Object> resolveArgument(MethodParameter parameter, ModelMap model, ServerWebExchange exchange) {
Class<?> paramType = parameter.getParameterType();
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
if (MultiValueMap.class.isAssignableFrom(paramType)) {
return Mono.just(queryParams);
}
else {
return Mono.just(queryParams.toSingleValueMap());
}
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Mono;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
/**
* Resolver for method arguments annotated with @{@link RequestParam}.
*
* <p>This resolver can also be created in default resolution mode in which
* simple types (int, long, etc.) not annotated with @{@link RequestParam} are
* also treated as request parameters with the parameter name derived from the
* argument name.
*
* <p>If the method parameter type is {@link Map}, the name specified in the
* annotation is used to resolve the request parameter String value. The value is
* then converted to a {@link Map} via type conversion assuming a suitable
* {@link Converter} has been registered. Or if a request parameter name is not
* specified the {@link RequestParamMapMethodArgumentResolver} is used instead
* to provide access to all request parameters in the form of a map.
*
* @author Rossen Stoyanchev
* @see RequestParamMapMethodArgumentResolver
*/
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
private final boolean useDefaultResolution;
/**
* @param conversionService for type conversion (to be replaced with WebDataBinder)
* @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 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(ConversionService conversionService,
ConfigurableBeanFactory beanFactory, boolean useDefaultResolution) {
super(conversionService, beanFactory);
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;
}
}
return (this.useDefaultResolution && BeanUtils.isSimpleProperty(parameter.getNestedParameterType()));
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
}
@Override
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
List<String> paramValues = exchange.getRequest().getQueryParams().get(name);
Object result = null;
if (paramValues != null) {
result = (paramValues.size() == 1 ? paramValues.get(0) : paramValues);
}
return Mono.justOrEmpty(result);
}
@Override
protected void handleMissingValue(String name, MethodParameter parameter, ServerWebExchange exchange) {
String type = parameter.getNestedParameterType().getSimpleName();
String reason = "Required " + type + " parameter '" + name + "' is not present";
throw new ServerWebInputException(reason, parameter);
}
private static class RequestParamNamedValueInfo extends NamedValueInfo {
public RequestParamNamedValueInfo() {
super("", false, ValueConstants.DEFAULT_NONE);
}
public RequestParamNamedValueInfo(RequestParam annotation) {
super(annotation.name(), annotation.required(), annotation.defaultValue());
}
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.util.Optional;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
/**
* Resolves method arguments annotated with an @{@link SessionAttribute}.
*
* @author Rossen Stoyanchev
* @see RequestAttributeMethodArgumentResolver
*/
public class SessionAttributeMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
public SessionAttributeMethodArgumentResolver(ConversionService conversionService,
ConfigurableBeanFactory beanFactory) {
super(conversionService, beanFactory);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(SessionAttribute.class);
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
SessionAttribute annot = parameter.getParameterAnnotation(SessionAttribute.class);
return new NamedValueInfo(annot.name(), annot.required(), ValueConstants.DEFAULT_NONE);
}
@Override
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange){
return exchange.getSession().map(session -> session.getAttribute(name))
.where(Optional::isPresent).map(Optional::get);
}
@Override
protected void handleMissingValue(String name, MethodParameter parameter) {
String type = parameter.getNestedParameterType().getSimpleName();
String reason = "Missing session attribute '" + name + "' of type " + type;
throw new ServerWebInputException(reason, parameter);
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.server;
import java.util.Optional;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
/**
* Exception for errors that fit response status 500 (bad request) for use in
* Spring Web applications. The exception provides additional fields (e.g.
* an optional {@link MethodParameter} if related to the error).
*
* @author Rossen Stoyanchev
*/
public class ServerErrorException extends ResponseStatusException {
private final MethodParameter parameter;
/**
* Constructor with an explanation only.
*/
public ServerErrorException(String reason) {
this(reason, null);
}
/**
* Constructor for a 500 error linked to a specific {@code MethodParameter}.
*/
public ServerErrorException(String reason, MethodParameter parameter) {
this(reason, parameter, null);
}
/**
* Constructor for a 500 error with a root cause.
*/
public ServerErrorException(String reason, MethodParameter parameter, Throwable cause) {
super(HttpStatus.INTERNAL_SERVER_ERROR, reason, cause);
this.parameter = parameter;
}
/**
* Return the {@code MethodParameter} associated with this error, if any.
*/
public Optional<MethodParameter> getMethodParameter() {
return Optional.ofNullable(this.parameter);
}
}
......@@ -28,14 +28,16 @@ import reactor.core.publisher.Mono;
import reactor.core.publisher.Signal;
import reactor.core.util.SignalKind;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.ModelMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.result.method.annotation.RequestParamArgumentResolver;
import org.springframework.web.reactive.result.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
......@@ -82,8 +84,10 @@ public class InvocableHandlerMethodTests {
@Test
public void resolveArgToZeroValues() throws Exception {
when(this.request.getURI()).thenReturn(new URI("http://localhost:8080/path"));
when(this.request.getQueryParams()).thenReturn(new LinkedMultiValueMap<>());
InvocableHandlerMethod hm = createHandlerMethod("singleArg", String.class);
hm.setHandlerMethodArgumentResolvers(Collections.singletonList(new RequestParamArgumentResolver()));
hm.setHandlerMethodArgumentResolvers(Collections.singletonList(
new RequestParamMethodArgumentResolver(new GenericConversionService(), null, false)));
Mono<HandlerResult> mono = hm.invokeForRequest(this.exchange, this.model);
HandlerResult value = mono.get();
......
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Mono;
import reactor.core.test.TestSubscriber;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.WebSession;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
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;
/**
* Base class for {@code @RequestAttribute} and {@code @SessionAttribute} method
* argument resolution tests.
*
* @author Rossen Stoyanchev
*/
public abstract class AbstractRequestAttributesArgumentResolverTests {
private HandlerMethodArgumentResolver resolver;
private ServerWebExchange exchange;
private Method handleMethod;
@Before @SuppressWarnings("ConfusingArgumentToVarargsMethod")
public void setUp() throws Exception {
this.resolver = createResolver();
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
WebSession webSession = mock(WebSession.class);
when(sessionManager.getSession(this.exchange)).thenReturn(Mono.just(webSession));
this.handleMethod = ReflectionUtils.findMethod(getClass(), getHandleMethodName(), (Class<?>[]) null);
}
protected abstract HandlerMethodArgumentResolver createResolver();
protected abstract String getHandleMethodName();
@Test
public void supportsParameter() throws Exception {
assertTrue(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 0)));
assertFalse(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 4)));
}
@Test
public void resolve() throws Exception {
MethodParameter param = initMethodParameter(0);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
TestSubscriber<Object> subscriber = new TestSubscriber<>();
mono.subscribeWith(subscriber);
subscriber.assertError(ServerWebInputException.class);
Foo foo = new Foo();
this.exchange.getAttributes().put("foo", foo);
mono = this.resolver.resolveArgument(param, null, this.exchange);
assertSame(foo, mono.get());
}
@Test
public void resolveWithName() throws Exception {
MethodParameter param = initMethodParameter(1);
Foo foo = new Foo();
this.exchange.getAttributes().put("specialFoo", foo);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
assertSame(foo, mono.get());
}
@Test
public void resolveNotRequired() throws Exception {
MethodParameter param = initMethodParameter(2);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
assertNull(mono.get());
Foo foo = new Foo();
this.exchange.getAttributes().put("foo", foo);
mono = this.resolver.resolveArgument(param, null, this.exchange);
assertSame(foo, mono.get());
}
@Test
public void resolveOptional() throws Exception {
MethodParameter param = initMethodParameter(3);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
assertNotNull(mono.get());
assertEquals(Optional.class, mono.get().getClass());
assertFalse(((Optional) mono.get()).isPresent());
Foo foo = new Foo();
this.exchange.getAttributes().put("foo", foo);
mono = this.resolver.resolveArgument(param, null, this.exchange);
assertNotNull(mono.get());
assertEquals(Optional.class, mono.get().getClass());
Optional optional = (Optional) mono.get();
assertTrue(optional.isPresent());
assertSame(foo, optional.get());
}
private MethodParameter initMethodParameter(int parameterIndex) {
MethodParameter param = new SynthesizingMethodParameter(this.handleMethod, parameterIndex);
param.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
GenericTypeResolver.resolveParameterType(param, this.resolver.getClass());
return param;
}
@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"})
private void handleWithRequestAttribute(
@RequestAttribute Foo foo,
@RequestAttribute("specialFoo") Foo namedFoo,
@RequestAttribute(name="foo", required = false) Foo notRequiredFoo,
@RequestAttribute(name="foo") Optional<Foo> optionalFoo) {
}
@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"})
private void handleWithSessionAttribute(
@SessionAttribute Foo foo,
@SessionAttribute("specialFoo") Foo namedFoo,
@SessionAttribute(name="foo", required = false) Foo notRequiredFoo,
@SessionAttribute(name="foo") Optional<Foo> optionalFoo) {
}
private static class Foo {
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Mono;
import reactor.core.test.TestSubscriber;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Test fixture with {@link CookieValueMethodArgumentResolver}.
*
* @author Rossen Stoyanchev
*/
public class CookieValueMethodArgumentResolverTests {
private CookieValueMethodArgumentResolver resolver;
private ServerWebExchange exchange;
private MethodParameter cookieParameter;
private MethodParameter cookieStringParameter;
private MethodParameter stringParameter;
@Before
public void setUp() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
ConversionService cs = new DefaultConversionService();
this.resolver = new CookieValueMethodArgumentResolver(cs, context.getBeanFactory());
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
Method method = getClass().getMethod("params", HttpCookie.class, String.class, String.class);
this.cookieParameter = new SynthesizingMethodParameter(method, 0);
this.cookieStringParameter = new SynthesizingMethodParameter(method, 1);
this.stringParameter = new SynthesizingMethodParameter(method, 2);
}
@Test
public void supportsParameter() {
assertTrue(this.resolver.supportsParameter(this.cookieParameter));
assertTrue(this.resolver.supportsParameter(this.cookieStringParameter));
assertFalse(this.resolver.supportsParameter(this.stringParameter));
}
@Test
public void resolveCookieArgument() {
HttpCookie expected = new HttpCookie("name", "foo");
this.exchange.getRequest().getCookies().add(expected.getName(), expected);
Mono<Object> mono = this.resolver.resolveArgument(this.cookieParameter, null, this.exchange);
assertEquals(expected, mono.get());
}
@Test
public void resolveCookieStringArgument() {
HttpCookie cookie = new HttpCookie("name", "foo");
this.exchange.getRequest().getCookies().add(cookie.getName(), cookie);
Mono<Object> mono = this.resolver.resolveArgument(this.cookieStringParameter, null, this.exchange);
assertEquals("Invalid result", cookie.getValue(), mono.get());
}
@Test
public void resolveCookieDefaultValue() {
Mono<Object> mono = this.resolver.resolveArgument(this.cookieStringParameter, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String);
assertEquals("bar", result);
}
@Test
public void notFound() {
Mono<Object> mono = resolver.resolveArgument(this.cookieParameter, null, this.exchange);
TestSubscriber<Object> subscriber = new TestSubscriber<>();
mono.subscribeWith(subscriber);
subscriber.assertError(ServerWebInputException.class);
}
@SuppressWarnings("unused")
public void params(
@CookieValue("name") HttpCookie cookie,
@CookieValue(name = "name", defaultValue = "bar") String cookieString,
String stringParam) {
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import org.junit.Before;
import org.junit.Test;
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.convert.ConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link ExpressionValueMethodArgumentResolver}.
*
* @author Rossen Stoyanchev
*/
public class ExpressionValueMethodArgumentResolverTests {
private ExpressionValueMethodArgumentResolver resolver;
private ServerWebExchange exchange;
private MethodParameter paramSystemProperty;
private MethodParameter paramNotSupported;
@Before
public void setUp() throws Exception {
ConversionService conversionService = new GenericConversionService();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
this.resolver = new ExpressionValueMethodArgumentResolver(conversionService, context.getBeanFactory());
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
Method method = getClass().getMethod("params", int.class, String.class);
this.paramSystemProperty = new MethodParameter(method, 0);
this.paramNotSupported = new MethodParameter(method, 1);
}
@Test
public void supportsParameter() throws Exception {
assertTrue(this.resolver.supportsParameter(this.paramSystemProperty));
assertFalse(this.resolver.supportsParameter(this.paramNotSupported));
}
@Test
public void resolveSystemProperty() throws Exception {
System.setProperty("systemProperty", "22");
try {
Mono<Object> mono = this.resolver.resolveArgument(this.paramSystemProperty, null, this.exchange);
Object value = mono.get();
assertEquals(22, value);
}
finally {
System.clearProperty("systemProperty");
}
}
// TODO: test with expression for ServerWebExchange
@SuppressWarnings("unused")
public void params(@Value("#{systemProperties.systemProperty}") int param1,
String notSupported) {
}
}
\ No newline at end of file
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
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.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link PathVariableMapMethodArgumentResolver}.
*
* @author Rossen Stoyanchev
*/
public class PathVariableMapMethodArgumentResolverTests {
private PathVariableMapMethodArgumentResolver resolver;
private ServerWebExchange exchange;
private MethodParameter paramMap;
private MethodParameter paramNamedMap;
private MethodParameter paramMapNoAnnot;
@Before
public void setUp() throws Exception {
this.resolver = new PathVariableMapMethodArgumentResolver();
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
Method method = getClass().getMethod("handle", Map.class, Map.class, Map.class);
this.paramMap = new MethodParameter(method, 0);
this.paramNamedMap = new MethodParameter(method, 1);
this.paramMapNoAnnot = new MethodParameter(method, 2);
}
@Test
public void supportsParameter() {
assertTrue(resolver.supportsParameter(paramMap));
assertFalse(resolver.supportsParameter(paramNamedMap));
assertFalse(resolver.supportsParameter(paramMapNoAnnot));
}
@Test
public void resolveArgument() throws Exception {
Map<String, String> uriTemplateVars = new HashMap<>();
uriTemplateVars.put("name1", "value1");
uriTemplateVars.put("name2", "value2");
this.exchange.getAttributes().put(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVars);
Mono<Object> mono = this.resolver.resolveArgument(this.paramMap, new ModelMap(), this.exchange);
Object result = mono.get();
assertEquals(uriTemplateVars, result);
}
@Test
public void resolveArgumentNoUriVars() throws Exception {
Mono<Object> mono = this.resolver.resolveArgument(this.paramMap, new ModelMap(), this.exchange);
Object result = mono.get();
assertEquals(Collections.emptyMap(), result);
}
@SuppressWarnings("unused")
public void handle(
@PathVariable Map<String, String> map,
@PathVariable(value = "name") Map<String, String> namedMap,
Map<String, String> mapWithoutAnnotat) {
}
}
\ No newline at end of file
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Mono;
import reactor.core.test.TestSubscriber;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.ServerErrorException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link PathVariableMethodArgumentResolver}.
*
* @author Rossen Stoyanchev
*/
public class PathVariableMethodArgumentResolverTests {
private PathVariableMethodArgumentResolver resolver;
private ServerWebExchange exchange;
private MethodParameter paramNamedString;
private MethodParameter paramString;
@Before
public void setUp() throws Exception {
ConversionService conversionService = new DefaultConversionService();
this.resolver = new PathVariableMethodArgumentResolver(conversionService, null);
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
Method method = getClass().getMethod("handle", String.class, String.class);
this.paramNamedString = new MethodParameter(method, 0);
this.paramString = new MethodParameter(method, 1);
}
@Test
public void supportsParameter() {
assertTrue(this.resolver.supportsParameter(this.paramNamedString));
assertFalse(this.resolver.supportsParameter(this.paramString));
}
@Test
public void resolveArgument() throws Exception {
Map<String, String> uriTemplateVars = new HashMap<>();
uriTemplateVars.put("name", "value");
this.exchange.getAttributes().put(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVars);
Mono<Object> mono = this.resolver.resolveArgument(this.paramNamedString, new ModelMap(), this.exchange);
Object result = mono.get();
assertTrue(result instanceof String);
assertEquals("value", result);
}
@Test
public void handleMissingValue() throws Exception {
Mono<Object> mono = this.resolver.resolveArgument(this.paramNamedString, new ModelMap(), this.exchange);
TestSubscriber<Object> subscriber = new TestSubscriber<>();
mono.subscribeWith(subscriber);
subscriber.assertError(ServerErrorException.class);
}
@SuppressWarnings("unused")
public void handle(@PathVariable(value = "name") String param1, String param2) {
}
}
\ No newline at end of file
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Mono;
import reactor.core.test.TestSubscriber;
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.annotation.SynthesizingMethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
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;
/**
* Unit tests for {@link RequestAttributeMethodArgumentResolver}.
* @author Rossen Stoyanchev
*/
public class RequestAttributeMethodArgumentResolverTests {
private RequestAttributeMethodArgumentResolver resolver;
private ServerWebExchange exchange;
private Method handleMethod;
@Before
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
public void setUp() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
ConversionService cs = new DefaultConversionService();
this.resolver = new RequestAttributeMethodArgumentResolver(cs, context.getBeanFactory());
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
this.handleMethod = ReflectionUtils.findMethod(getClass(), "handleWithRequestAttribute", (Class<?>[]) null);
}
@Test
public void supportsParameter() throws Exception {
assertTrue(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 0)));
assertFalse(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 4)));
}
@Test
public void resolve() throws Exception {
MethodParameter param = initMethodParameter(0);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
TestSubscriber<Object> subscriber = new TestSubscriber<>();
mono.subscribeWith(subscriber);
subscriber.assertError(ServerWebInputException.class);
Foo foo = new Foo();
this.exchange.getAttributes().put("foo", foo);
mono = this.resolver.resolveArgument(param, null, this.exchange);
assertSame(foo, mono.get());
}
@Test
public void resolveWithName() throws Exception {
MethodParameter param = initMethodParameter(1);
Foo foo = new Foo();
this.exchange.getAttributes().put("specialFoo", foo);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
assertSame(foo, mono.get());
}
@Test
public void resolveNotRequired() throws Exception {
MethodParameter param = initMethodParameter(2);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
assertNull(mono.get());
Foo foo = new Foo();
this.exchange.getAttributes().put("foo", foo);
mono = this.resolver.resolveArgument(param, null, this.exchange);
assertSame(foo, mono.get());
}
@Test
public void resolveOptional() throws Exception {
MethodParameter param = initMethodParameter(3);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
assertNotNull(mono.get());
assertEquals(Optional.class, mono.get().getClass());
assertFalse(((Optional) mono.get()).isPresent());
Foo foo = new Foo();
this.exchange.getAttributes().put("foo", foo);
mono = this.resolver.resolveArgument(param, null, this.exchange);
assertNotNull(mono.get());
assertEquals(Optional.class, mono.get().getClass());
Optional optional = (Optional) mono.get();
assertTrue(optional.isPresent());
assertSame(foo, optional.get());
}
private MethodParameter initMethodParameter(int parameterIndex) {
MethodParameter param = new SynthesizingMethodParameter(this.handleMethod, parameterIndex);
param.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
GenericTypeResolver.resolveParameterType(param, this.resolver.getClass());
return param;
}
@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"})
private void handleWithRequestAttribute(
@RequestAttribute Foo foo,
@RequestAttribute("specialFoo") Foo namedFoo,
@RequestAttribute(name="foo", required = false) Foo notRequiredFoo,
@RequestAttribute(name="foo") Optional<Foo> optionalFoo) {
}
private static class Foo {
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link RequestHeaderMapMethodArgumentResolver}.
*
* @author Rossen Stoyanchev
*/
public class RequestHeaderMapMethodArgumentResolverTests {
private RequestHeaderMapMethodArgumentResolver resolver;
private MethodParameter paramMap;
private MethodParameter paramMultiValueMap;
private MethodParameter paramHttpHeaders;
private MethodParameter paramUnsupported;
private ServerWebExchange exchange;
@Before
public void setUp() throws Exception {
resolver = new RequestHeaderMapMethodArgumentResolver();
Method method = getClass().getMethod("params", Map.class, MultiValueMap.class, HttpHeaders.class, Map.class);
paramMap = new SynthesizingMethodParameter(method, 0);
paramMultiValueMap = new SynthesizingMethodParameter(method, 1);
paramHttpHeaders = new SynthesizingMethodParameter(method, 2);
paramUnsupported = new SynthesizingMethodParameter(method, 3);
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
}
@Test
public void supportsParameter() {
assertTrue("Map parameter not supported", resolver.supportsParameter(paramMap));
assertTrue("MultiValueMap parameter not supported", resolver.supportsParameter(paramMultiValueMap));
assertTrue("HttpHeaders parameter not supported", resolver.supportsParameter(paramHttpHeaders));
assertFalse("non-@RequestParam map supported", resolver.supportsParameter(paramUnsupported));
}
@Test
public void resolveMapArgument() throws Exception {
String name = "foo";
String value = "bar";
Map<String, String> expected = Collections.singletonMap(name, value);
this.exchange.getRequest().getHeaders().add(name, value);
Mono<Object> mono = this.resolver.resolveArgument(paramMap, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof Map);
assertEquals("Invalid result", expected, result);
}
@Test
public void resolveMultiValueMapArgument() throws Exception {
String name = "foo";
String value1 = "bar";
String value2 = "baz";
this.exchange.getRequest().getHeaders().add(name, value1);
this.exchange.getRequest().getHeaders().add(name, value2);
MultiValueMap<String, String> expected = new LinkedMultiValueMap<>(1);
expected.add(name, value1);
expected.add(name, value2);
Mono<Object> mono = this.resolver.resolveArgument(paramMultiValueMap, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof MultiValueMap);
assertEquals("Invalid result", expected, result);
}
@Test
public void resolveHttpHeadersArgument() throws Exception {
String name = "foo";
String value1 = "bar";
String value2 = "baz";
this.exchange.getRequest().getHeaders().add(name, value1);
this.exchange.getRequest().getHeaders().add(name, value2);
HttpHeaders expected = new HttpHeaders();
expected.add(name, value1);
expected.add(name, value2);
Mono<Object> mono = this.resolver.resolveArgument(paramHttpHeaders, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof HttpHeaders);
assertEquals("Invalid result", expected, result);
}
@SuppressWarnings("unused")
public void params(@RequestHeader Map<?, ?> param1,
@RequestHeader MultiValueMap<?, ?> param2,
@RequestHeader HttpHeaders param3,
Map<?,?> unsupported) {
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Mono;
import reactor.core.test.TestSubscriber;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link RequestHeaderMethodArgumentResolver}.
*
* @author Rossen Stoyanchev
*/
public class RequestHeaderMethodArgumentResolverTests {
private RequestHeaderMethodArgumentResolver resolver;
private MethodParameter paramNamedDefaultValueStringHeader;
private MethodParameter paramNamedValueStringArray;
private MethodParameter paramSystemProperty;
private MethodParameter paramResolvedNameWithExpression;
private MethodParameter paramResolvedNameWithPlaceholder;
private MethodParameter paramNamedValueMap;
private MethodParameter paramDate;
private MethodParameter paramInstant;
private ServerWebExchange exchange;
@Before
public void setUp() throws Exception {
ConversionService conversionService = new DefaultFormattingConversionService();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
this.resolver = new RequestHeaderMethodArgumentResolver(conversionService, context.getBeanFactory());
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
Method method = ReflectionUtils.findMethod(getClass(), "params", null);
this.paramNamedDefaultValueStringHeader = new SynthesizingMethodParameter(method, 0);
this.paramNamedValueStringArray = new SynthesizingMethodParameter(method, 1);
this.paramSystemProperty = new SynthesizingMethodParameter(method, 2);
this.paramResolvedNameWithExpression = new SynthesizingMethodParameter(method, 3);
this.paramResolvedNameWithPlaceholder = new SynthesizingMethodParameter(method, 4);
this.paramNamedValueMap = new SynthesizingMethodParameter(method, 5);
this.paramDate = new SynthesizingMethodParameter(method, 6);
this.paramInstant = new SynthesizingMethodParameter(method, 7);
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
}
@Test
public void supportsParameter() {
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));
}
@Test
public void resolveStringArgument() throws Exception {
String expected = "foo";
this.exchange.getRequest().getHeaders().add("name", expected);
Mono<Object> mono = this.resolver.resolveArgument(paramNamedDefaultValueStringHeader, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String);
assertEquals(expected, result);
}
@Test
public void resolveStringArrayArgument() throws Exception {
String[] expected = new String[] {"foo", "bar"};
this.exchange.getRequest().getHeaders().put("name", Arrays.asList(expected));
Mono<Object> mono = this.resolver.resolveArgument(paramNamedValueStringArray, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String[]);
assertArrayEquals(expected, (String[]) result);
}
@Test
public void resolveDefaultValue() throws Exception {
Mono<Object> mono = this.resolver.resolveArgument(paramNamedDefaultValueStringHeader, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String);
assertEquals("bar", result);
}
@Test
public void resolveDefaultValueFromSystemProperty() throws Exception {
System.setProperty("systemProperty", "bar");
try {
Mono<Object> mono = this.resolver.resolveArgument(paramSystemProperty, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String);
assertEquals("bar", result);
}
finally {
System.clearProperty("systemProperty");
}
}
@Test
public void resolveNameFromSystemPropertyThroughExpression() throws Exception {
String expected = "foo";
this.exchange.getRequest().getHeaders().add("bar", expected);
System.setProperty("systemProperty", "bar");
try {
Mono<Object> mono = this.resolver.resolveArgument(paramResolvedNameWithExpression, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String);
assertEquals(expected, result);
}
finally {
System.clearProperty("systemProperty");
}
}
@Test
public void resolveNameFromSystemPropertyThroughPlaceholder() throws Exception {
String expected = "foo";
this.exchange.getRequest().getHeaders().add("bar", expected);
System.setProperty("systemProperty", "bar");
try {
Mono<Object> mono = this.resolver.resolveArgument(paramResolvedNameWithPlaceholder, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String);
assertEquals(expected, result);
}
finally {
System.clearProperty("systemProperty");
}
}
@Test
public void notFound() throws Exception {
Mono<Object> mono = resolver.resolveArgument(paramNamedValueStringArray, null, this.exchange);
TestSubscriber<Object> subscriber = new TestSubscriber<>();
mono.subscribeWith(subscriber);
subscriber.assertError(ServerWebInputException.class);
}
@Test
public void dateConversion() throws Exception {
String rfc1123val = "Thu, 21 Apr 2016 17:11:08 +0100";
this.exchange.getRequest().getHeaders().add("name", rfc1123val);
Mono<Object> mono = this.resolver.resolveArgument(paramDate, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof Date);
assertEquals(new Date(rfc1123val), result);
}
@Test
public void instantConversion() throws Exception {
String rfc1123val = "Thu, 21 Apr 2016 17:11:08 +0100";
this.exchange.getRequest().getHeaders().add("name", rfc1123val);
Mono<Object> mono = this.resolver.resolveArgument(paramInstant, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof Instant);
assertEquals(Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(rfc1123val)), result);
}
@SuppressWarnings("unused")
public void params(
@RequestHeader(name = "name", defaultValue = "bar") String param1,
@RequestHeader("name") String[] param2,
@RequestHeader(name = "name", defaultValue="#{systemProperties.systemProperty}") String param3,
@RequestHeader("#{systemProperties.systemProperty}") String param4,
@RequestHeader("${systemProperty}") String param5,
@RequestHeader("name") Map<?, ?> unsupported,
@RequestHeader("name") Date dateParam,
@RequestHeader("name") Instant instantParam) {
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Mono;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link RequestParamMapMethodArgumentResolver}.
*
* @author Rossen Stoyanchev
*/
public class RequestParamMapMethodArgumentResolverTests {
private RequestParamMapMethodArgumentResolver resolver;
private ServerWebExchange exchange;
private MethodParameter paramMap;
private MethodParameter paramMultiValueMap;
private MethodParameter paramNamedMap;
private MethodParameter paramMapWithoutAnnot;
@Before
public void setUp() throws Exception {
this.resolver = new RequestParamMapMethodArgumentResolver();
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
Method method = getClass().getMethod("params", Map.class, MultiValueMap.class, Map.class, Map.class);
this.paramMap = new SynthesizingMethodParameter(method, 0);
this.paramMultiValueMap = new SynthesizingMethodParameter(method, 1);
this.paramNamedMap = new SynthesizingMethodParameter(method, 2);
this.paramMapWithoutAnnot = new SynthesizingMethodParameter(method, 3);
}
@Test
public void supportsParameter() {
assertTrue(this.resolver.supportsParameter(this.paramMap));
assertTrue(this.resolver.supportsParameter(this.paramMultiValueMap));
assertFalse(this.resolver.supportsParameter(this.paramNamedMap));
assertFalse(this.resolver.supportsParameter(this.paramMapWithoutAnnot));
}
@Test
public void resolveMapArgument() throws Exception {
String name = "foo";
String value = "bar";
this.exchange.getRequest().getQueryParams().set(name, value);
Map<String, String> expected = Collections.singletonMap(name, value);
Mono<Object> mono = resolver.resolveArgument(paramMap, null, exchange);
Object result = mono.get();
assertTrue(result instanceof Map);
assertEquals(expected, result);
}
@Test
public void resolveMultiValueMapArgument() throws Exception {
String name = "foo";
String value1 = "bar";
String value2 = "baz";
this.exchange.getRequest().getQueryParams().put(name, Arrays.asList(value1, value2));
MultiValueMap<String, String> expected = new LinkedMultiValueMap<>(1);
expected.add(name, value1);
expected.add(name, value2);
Mono<Object> mono = this.resolver.resolveArgument(this.paramMultiValueMap, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof MultiValueMap);
assertEquals(expected, result);
}
@SuppressWarnings("unused")
public void params(@RequestParam Map<?, ?> param1,
@RequestParam MultiValueMap<?, ?> param2,
@RequestParam("name") Map<?, ?> param3,
Map<?, ?> param4) {
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Mono;
import reactor.core.test.TestSubscriber;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link RequestParamMethodArgumentResolver}.
*
* @author Rossen Stoyanchev
*/
public class RequestParamMethodArgumentResolverTests {
private RequestParamMethodArgumentResolver resolver;
private ServerWebExchange exchange;
private MethodParameter paramNamedDefaultValueString;
private MethodParameter paramNamedStringArray;
private MethodParameter paramNamedMap;
private MethodParameter paramMap;
private MethodParameter paramStringNotAnnot;
private MethodParameter paramRequired;
private MethodParameter paramNotRequired;
private MethodParameter paramOptional;
@Before @SuppressWarnings("ConfusingArgumentToVarargsMethod")
public void setUp() throws Exception {
ConversionService conversionService = new DefaultConversionService();
this.resolver = new RequestParamMethodArgumentResolver(conversionService, null, true);
ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
Method method = ReflectionUtils.findMethod(getClass(), "handle", (Class<?>[]) null);
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
this.paramNamedDefaultValueString = new SynthesizingMethodParameter(method, 0);
this.paramNamedStringArray = new SynthesizingMethodParameter(method, 1);
this.paramNamedMap = new SynthesizingMethodParameter(method, 2);
this.paramMap = new SynthesizingMethodParameter(method, 3);
this.paramStringNotAnnot = new SynthesizingMethodParameter(method, 4);
this.paramStringNotAnnot.initParameterNameDiscovery(paramNameDiscoverer);
this.paramRequired = new SynthesizingMethodParameter(method, 5);
this.paramNotRequired = new SynthesizingMethodParameter(method, 6);
this.paramOptional = new SynthesizingMethodParameter(method, 7);
}
@Test
public void supportsParameter() {
this.resolver = new RequestParamMethodArgumentResolver(new GenericConversionService(), null, true);
assertTrue(this.resolver.supportsParameter(this.paramNamedDefaultValueString));
assertTrue(this.resolver.supportsParameter(this.paramNamedStringArray));
assertTrue(this.resolver.supportsParameter(this.paramNamedMap));
assertFalse(this.resolver.supportsParameter(this.paramMap));
assertTrue(this.resolver.supportsParameter(this.paramStringNotAnnot));
assertTrue(this.resolver.supportsParameter(this.paramRequired));
assertTrue(this.resolver.supportsParameter(this.paramNotRequired));
assertTrue(this.resolver.supportsParameter(this.paramOptional));
this.resolver = new RequestParamMethodArgumentResolver(new GenericConversionService(), null, false);
assertFalse(this.resolver.supportsParameter(this.paramStringNotAnnot));
}
@Test
public void resolveString() throws Exception {
String expected = "foo";
this.exchange.getRequest().getQueryParams().set("name", expected);
Mono<Object> mono = this.resolver.resolveArgument(this.paramNamedDefaultValueString, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String);
assertEquals("Invalid result", expected, result);
}
@Test
public void resolveStringArray() throws Exception {
String[] expected = {"foo", "bar"};
this.exchange.getRequest().getQueryParams().put("name", Arrays.asList(expected));
Mono<Object> mono = this.resolver.resolveArgument(this.paramNamedStringArray, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String[]);
assertArrayEquals(expected, (String[]) result);
}
@Test
public void resolveDefaultValue() throws Exception {
Mono<Object> mono = this.resolver.resolveArgument(paramNamedDefaultValueString, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String);
assertEquals("Invalid result", "bar", result);
}
@Test
public void missingRequestParam() throws Exception {
Mono<Object> mono = this.resolver.resolveArgument(paramNamedStringArray, null, this.exchange);
TestSubscriber<Object> subscriber = new TestSubscriber<>();
mono.subscribeWith(subscriber);
subscriber.assertError(ServerWebInputException.class);
}
@Test
public void resolveSimpleTypeParam() throws Exception {
this.exchange.getRequest().getQueryParams().set("stringNotAnnot", "plainValue");
Mono<Object> mono = this.resolver.resolveArgument(paramStringNotAnnot, null, this.exchange);
Object result = mono.get();
assertTrue(result instanceof String);
assertEquals("plainValue", result);
}
@Test // SPR-8561
public void resolveSimpleTypeParamToNull() throws Exception {
Mono<Object> mono = this.resolver.resolveArgument(paramStringNotAnnot, null, this.exchange);
Object result = mono.get();
assertNull(result);
}
@Test // SPR-10180
public void resolveEmptyValueToDefault() throws Exception {
this.exchange.getRequest().getQueryParams().set("name", "");
Mono<Object> mono = this.resolver.resolveArgument(paramNamedDefaultValueString, null, this.exchange);
Object result = mono.get();
assertEquals("bar", result);
}
@Test
public void resolveEmptyValueWithoutDefault() throws Exception {
this.exchange.getRequest().getQueryParams().set("stringNotAnnot", "");
Mono<Object> mono = this.resolver.resolveArgument(paramStringNotAnnot, null, this.exchange);
Object result = mono.get();
assertEquals("", result);
}
@Test
public void resolveEmptyValueRequiredWithoutDefault() throws Exception {
this.exchange.getRequest().getQueryParams().set("name", "");
Mono<Object> mono = this.resolver.resolveArgument(paramRequired, null, this.exchange);
Object result = mono.get();
assertEquals("", result);
}
@Test
public void resolveOptionalParamValue() throws Exception {
Mono<Object> mono = this.resolver.resolveArgument(paramOptional, null, this.exchange);
Object result = mono.get();
assertEquals(Optional.empty(), result);
this.exchange.getRequest().getQueryParams().set("name", "123");
mono = resolver.resolveArgument(paramOptional, null, this.exchange);
result = mono.get();
assertEquals(Optional.class, result.getClass());
Optional<?> value = (Optional<?>) result;
assertTrue(value.isPresent());
assertEquals(123, value.get());
}
@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"})
public void handle(
@RequestParam(name = "name", defaultValue = "bar") String param1,
@RequestParam("name") String[] param2,
@RequestParam("name") Map<?, ?> param3,
@RequestParam Map<?, ?> param4,
String stringNotAnnot,
@RequestParam("name") String paramRequired,
@RequestParam(name = "name", required = false) String paramNotRequired,
@RequestParam("name") Optional<Integer> paramOptional) {
}
}
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.result.method.annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Mono;
import reactor.core.test.TestSubscriber;
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.annotation.SynthesizingMethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.MockServerHttpRequest;
import org.springframework.http.server.reactive.MockServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.WebSession;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.WebSessionManager;
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.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Unit tests for {@link SessionAttributeMethodArgumentResolver}.
* @author Rossen Stoyanchev
*/
public class SessionAttributeMethodArgumentResolverTests {
private SessionAttributeMethodArgumentResolver resolver;
private ServerWebExchange exchange;
private WebSession session;
private Method handleMethod;
@Before
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
public void setUp() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
ConversionService cs = new DefaultConversionService();
this.resolver = new SessionAttributeMethodArgumentResolver(cs, context.getBeanFactory());
ServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/"));
WebSessionManager sessionManager = mock(WebSessionManager.class);
this.exchange = new DefaultServerWebExchange(request, new MockServerHttpResponse(), sessionManager);
this.session = mock(WebSession.class);
when(sessionManager.getSession(this.exchange)).thenReturn(Mono.just(this.session));
when(this.session.getAttribute(any())).thenReturn(Optional.empty());
this.handleMethod = ReflectionUtils.findMethod(getClass(), "handleWithSessionAttribute", (Class<?>[]) null);
}
@Test
public void supportsParameter() throws Exception {
assertTrue(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 0)));
assertFalse(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 4)));
}
@Test
public void resolve() throws Exception {
MethodParameter param = initMethodParameter(0);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
TestSubscriber<Object> subscriber = new TestSubscriber<>();
mono.subscribeWith(subscriber);
subscriber.assertError(ServerWebInputException.class);
Foo foo = new Foo();
when(this.session.getAttribute("foo")).thenReturn(Optional.of(foo));
mono = this.resolver.resolveArgument(param, null, this.exchange);
assertSame(foo, mono.get());
}
@Test
public void resolveWithName() throws Exception {
MethodParameter param = initMethodParameter(1);
Foo foo = new Foo();
when(this.session.getAttribute("specialFoo")).thenReturn(Optional.of(foo));
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
assertSame(foo, mono.get());
}
@Test
public void resolveNotRequired() throws Exception {
MethodParameter param = initMethodParameter(2);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
assertNull(mono.get());
Foo foo = new Foo();
when(this.session.getAttribute("foo")).thenReturn(Optional.of(foo));
mono = this.resolver.resolveArgument(param, null, this.exchange);
assertSame(foo, mono.get());
}
@Test
public void resolveOptional() throws Exception {
MethodParameter param = initMethodParameter(3);
Mono<Object> mono = this.resolver.resolveArgument(param, null, this.exchange);
assertNotNull(mono.get());
assertEquals(Optional.class, mono.get().getClass());
assertFalse(((Optional) mono.get()).isPresent());
Foo foo = new Foo();
when(this.session.getAttribute("foo")).thenReturn(Optional.of(foo));
mono = this.resolver.resolveArgument(param, null, this.exchange);
assertNotNull(mono.get());
assertEquals(Optional.class, mono.get().getClass());
Optional optional = (Optional) mono.get();
assertTrue(optional.isPresent());
assertSame(foo, optional.get());
}
private MethodParameter initMethodParameter(int parameterIndex) {
MethodParameter param = new SynthesizingMethodParameter(this.handleMethod, parameterIndex);
param.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
GenericTypeResolver.resolveParameterType(param, this.resolver.getClass());
return param;
}
@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"})
private void handleWithSessionAttribute(
@SessionAttribute Foo foo,
@SessionAttribute("specialFoo") Foo namedFoo,
@SessionAttribute(name="foo", required = false) Foo notRequiredFoo,
@SessionAttribute(name="foo") Optional<Foo> optionalFoo) {
}
private static class Foo {
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册