Remove ServerWebExchange#getRequestParams
Issue: SPR-15508
This commit is contained in:
parent
7f19e57d9d
commit
1881727b37
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.springframework.web.bind.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
|
@ -24,10 +23,10 @@ import java.util.TreeMap;
|
|||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.http.codec.multipart.Part;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
|
|
@ -39,6 +38,7 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
*/
|
||||
public class WebExchangeDataBinder extends WebDataBinder {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance, with default object name.
|
||||
* @param target the target object to bind onto (or {@code null} if the
|
||||
|
|
@ -61,62 +61,52 @@ public class WebExchangeDataBinder extends WebDataBinder {
|
|||
|
||||
|
||||
/**
|
||||
* Bind the URL query parameters or form data of the body of the given request
|
||||
* to this binder's target. The request body is parsed if the Content-Type
|
||||
* is {@code "application/x-www-form-urlencoded"}.
|
||||
* Bind query params, form data, and or multipart form data to the binder target.
|
||||
* @param exchange the current exchange.
|
||||
* @return a {@code Mono<Void>} when binding is complete
|
||||
*/
|
||||
public Mono<Void> bind(ServerWebExchange exchange) {
|
||||
return exchange.getRequestParams()
|
||||
.map(this::getParamsToBind)
|
||||
.doOnNext(values -> values.putAll(getMultipartFiles(exchange)))
|
||||
.doOnNext(values -> values.putAll(getExtraValuesToBind(exchange)))
|
||||
.flatMap(values -> {
|
||||
doBind(new MutablePropertyValues(values));
|
||||
return Mono.empty();
|
||||
return getValuesToBind(exchange)
|
||||
.doOnNext(values -> doBind(new MutablePropertyValues(values)))
|
||||
.then();
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected method to obtain the values for data binding. By default this
|
||||
* method delegates to {@link #extractValuesToBind(ServerWebExchange)}.
|
||||
*/
|
||||
protected Mono<Map<String, Object>> getValuesToBind(ServerWebExchange exchange) {
|
||||
return extractValuesToBind(exchange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine query params and form data for multipart form data from the body
|
||||
* of the request into a {@code Map<String, Object>} of values to use for
|
||||
* data binding purposes.
|
||||
*
|
||||
* @param exchange the current exchange
|
||||
* @return a {@code Mono} with the values to bind
|
||||
*/
|
||||
public static Mono<Map<String, Object>> extractValuesToBind(ServerWebExchange exchange) {
|
||||
|
||||
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
|
||||
Mono<MultiValueMap<String, String>> formData = exchange.getFormData();
|
||||
Mono<MultiValueMap<String, Part>> multipartData = exchange.getMultipartData();
|
||||
|
||||
return Mono.when(Mono.just(queryParams), formData, multipartData)
|
||||
.map(tuple -> {
|
||||
Map<String, Object> result = new TreeMap<>();
|
||||
tuple.getT1().forEach((key, values) -> addBindValue(result, key, values));
|
||||
tuple.getT2().forEach((key, values) -> addBindValue(result, key, values));
|
||||
tuple.getT3().forEach((key, values) -> addBindValue(result, key, values));
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private Map<String, Object> getParamsToBind(MultiValueMap<String, String> params) {
|
||||
Map<String, Object> result = new TreeMap<>();
|
||||
for (Map.Entry<String, List<String>> entry : params.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
List<String> values = entry.getValue();
|
||||
if (CollectionUtils.isEmpty(values)) {
|
||||
// Do nothing, no values found at all.
|
||||
}
|
||||
else if (values.size() == 1) {
|
||||
result.put(name, values.get(0));
|
||||
}
|
||||
else {
|
||||
result.put(name, values);
|
||||
}
|
||||
private static void addBindValue(Map<String, Object> params, String key, List<?> values) {
|
||||
if (!CollectionUtils.isEmpty(values)) {
|
||||
params.put(key, values.size() == 1 ? values.get(0) : values);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all multipart files contained in the given request, if any (in case
|
||||
* of a multipart request).
|
||||
* <p>Multipart files will only be added to the property values if they
|
||||
* are not empty or if we're configured to bind empty multipart files too.
|
||||
* @param exchange the current exchange
|
||||
* @return Map of field name String to MultipartFile object
|
||||
*/
|
||||
protected Map<String, List<MultipartFile>> getMultipartFiles(ServerWebExchange exchange) {
|
||||
// TODO
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point that subclasses can use to add extra bind values for a
|
||||
* request. Invoked before {@link #doBind(MutablePropertyValues)}.
|
||||
* <p>The default implementation is empty.
|
||||
* @param exchange the current exchange
|
||||
*/
|
||||
protected Map<String, ?> getExtraValuesToBind(ServerWebExchange exchange) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -80,24 +80,23 @@ public interface ServerWebExchange {
|
|||
/**
|
||||
* Return the form data from the body of the request if the Content-Type is
|
||||
* {@code "application/x-www-form-urlencoded"} or an empty map otherwise.
|
||||
* This method may be called multiple times.
|
||||
*
|
||||
* <p><strong>Note:</strong> calling this method causes the request body to
|
||||
* be read and parsed in full and the resulting {@code MultiValueMap} is
|
||||
* cached so that this method is safe to call more than once.
|
||||
*/
|
||||
Mono<MultiValueMap<String, String>> getFormData();
|
||||
|
||||
/**
|
||||
* Return the parts of a multipart request if the Content-Type is
|
||||
* {@code "multipart/form-data"} or an empty map otherwise.
|
||||
* This method may be called multiple times.
|
||||
*
|
||||
* <p><strong>Note:</strong> calling this method causes the request body to
|
||||
* be read and parsed in full and the resulting {@code MultiValueMap} is
|
||||
* cached so that this method is safe to call more than once.
|
||||
*/
|
||||
Mono<MultiValueMap<String, Part>> getMultipartData();
|
||||
|
||||
/**
|
||||
* Return a combined map that represents both
|
||||
* {@link ServerHttpRequest#getQueryParams()} and {@link #getFormData()}
|
||||
* or an empty map.
|
||||
*/
|
||||
Mono<MultiValueMap<String, String>> getRequestParams();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the one of the {@code checkNotModified} methods
|
||||
* in this contract were used and they returned true.
|
||||
|
|
|
|||
|
|
@ -99,11 +99,6 @@ public class ServerWebExchangeDecorator implements ServerWebExchange {
|
|||
return getDelegate().getMultipartData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<MultiValueMap<String, String>> getRequestParams() {
|
||||
return getDelegate().getRequestParams();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNotModified() {
|
||||
return getDelegate().isNotModified();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -88,8 +88,6 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
|
||||
private final Mono<MultiValueMap<String, Part>> multipartDataMono;
|
||||
|
||||
private final Mono<MultiValueMap<String, String>> requestParamsMono;
|
||||
|
||||
private volatile boolean notModified;
|
||||
|
||||
|
||||
|
|
@ -109,8 +107,6 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
this.sessionMono = sessionManager.getSession(this).cache();
|
||||
this.formDataMono = initFormData(request, codecConfigurer);
|
||||
this.multipartDataMono = initMultipartData(request, codecConfigurer);
|
||||
this.requestParamsMono = initRequestParams(request, this.formDataMono);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -165,20 +161,6 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
return EMPTY_MULTIPART_DATA;
|
||||
}
|
||||
|
||||
private static Mono<MultiValueMap<String, String>> initRequestParams(
|
||||
ServerHttpRequest request, Mono<MultiValueMap<String, String>> formDataMono) {
|
||||
|
||||
return formDataMono
|
||||
.map(formData -> {
|
||||
MultiValueMap<String, String> result = new LinkedMultiValueMap<>();
|
||||
result.putAll(request.getQueryParams());
|
||||
result.putAll(formData);
|
||||
return CollectionUtils.unmodifiableMultiValueMap(result);
|
||||
})
|
||||
.defaultIfEmpty(request.getQueryParams())
|
||||
.cache();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ServerHttpRequest getRequest() {
|
||||
|
|
@ -228,11 +210,6 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
return this.multipartDataMono;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<MultiValueMap<String, String>> getRequestParams() {
|
||||
return this.requestParamsMono;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNotModified() {
|
||||
return this.notModified;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ import java.util.Collections;
|
|||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
|
|
@ -143,18 +141,12 @@ public final class ParamsRequestCondition extends AbstractRequestCondition<Param
|
|||
|
||||
@Override
|
||||
protected boolean matchName(ServerWebExchange exchange) {
|
||||
return getRequestParams(exchange).containsKey(this.name);
|
||||
return exchange.getRequest().getQueryParams().containsKey(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchValue(ServerWebExchange exchange) {
|
||||
return this.value.equals(getRequestParams(exchange).getFirst(this.name));
|
||||
}
|
||||
|
||||
private MultiValueMap<String, String> getRequestParams(ServerWebExchange exchange) {
|
||||
MultiValueMap<String, String> params = exchange.getRequestParams().subscribe().peek();
|
||||
Assert.notNull(params, "Expected form data (if any) to be parsed.");
|
||||
return params;
|
||||
return this.value.equals(exchange.getRequest().getQueryParams().getFirst(this.name));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -258,32 +258,26 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
|||
logger.debug("Looking up handler method for path " + lookupPath);
|
||||
}
|
||||
this.mappingRegistry.acquireReadLock();
|
||||
|
||||
try {
|
||||
// Ensure form data is parsed for "params" conditions...
|
||||
return exchange.getRequestParams()
|
||||
.then(exchange.getMultipartData())
|
||||
.then(Mono.defer(() -> {
|
||||
HandlerMethod handlerMethod = null;
|
||||
try {
|
||||
handlerMethod = lookupHandlerMethod(lookupPath, exchange);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (handlerMethod != null) {
|
||||
logger.debug("Returning handler method [" + handlerMethod + "]");
|
||||
}
|
||||
else {
|
||||
logger.debug("Did not find handler method for [" + lookupPath + "]");
|
||||
}
|
||||
}
|
||||
if (handlerMethod != null) {
|
||||
handlerMethod = handlerMethod.createWithResolvedBean();
|
||||
}
|
||||
return Mono.justOrEmpty(handlerMethod);
|
||||
}));
|
||||
HandlerMethod handlerMethod;
|
||||
try {
|
||||
handlerMethod = lookupHandlerMethod(lookupPath, exchange);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (handlerMethod != null) {
|
||||
logger.debug("Returning handler method [" + handlerMethod + "]");
|
||||
}
|
||||
else {
|
||||
logger.debug("Did not find handler method for [" + lookupPath + "]");
|
||||
}
|
||||
}
|
||||
if (handlerMethod != null) {
|
||||
handlerMethod = handlerMethod.createWithResolvedBean();
|
||||
}
|
||||
return Mono.justOrEmpty(handlerMethod);
|
||||
}
|
||||
finally {
|
||||
this.mappingRegistry.releaseReadLock();
|
||||
|
|
|
|||
|
|
@ -133,8 +133,8 @@ class ControllerMethodResolver {
|
|||
|
||||
// Annotation-based...
|
||||
registrar.add(new RequestParamMethodArgumentResolver(beanFactory, reactiveRegistry, false));
|
||||
registrar.add(new RequestPartMethodArgumentResolver(beanFactory, reactiveRegistry, false));
|
||||
registrar.add(new RequestParamMapMethodArgumentResolver(reactiveRegistry));
|
||||
registrar.add(new RequestPartMethodArgumentResolver(reactiveRegistry));
|
||||
registrar.add(new PathVariableMethodArgumentResolver(beanFactory, reactiveRegistry));
|
||||
registrar.add(new PathVariableMapMethodArgumentResolver(reactiveRegistry));
|
||||
registrar.addIfRequestBody(readers -> new RequestBodyArgumentResolver(readers, reactiveRegistry));
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
|||
}
|
||||
|
||||
// A single data class constructor -> resolve constructor arguments from request parameters.
|
||||
return exchange.getRequestParams().map(requestParams -> {
|
||||
return WebExchangeDataBinder.extractValuesToBind(exchange).map(bindValues -> {
|
||||
ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class);
|
||||
String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor));
|
||||
Assert.state(paramNames != null, () -> "Cannot resolve parameter names for constructor " + ctor);
|
||||
|
|
@ -226,13 +226,9 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
|||
Object[] args = new Object[paramTypes.length];
|
||||
WebDataBinder binder = context.createDataBinder(exchange, null, attributeName);
|
||||
for (int i = 0; i < paramNames.length; i++) {
|
||||
List<String> paramValues = requestParams.get(paramNames[i]);
|
||||
Object paramValue = null;
|
||||
if (paramValues != null) {
|
||||
paramValue = (paramValues.size() == 1 ? paramValues.get(0) :
|
||||
paramValues.toArray(new String[paramValues.size()]));
|
||||
}
|
||||
args[i] = binder.convertIfNecessary(paramValue, paramTypes[i], new MethodParameter(ctor, i));
|
||||
Object value = bindValues.get(paramNames[i]);
|
||||
value = (value != null && value instanceof List ? ((List<?>) value).toArray() : value);
|
||||
args[i] = binder.convertIfNecessary(value, paramTypes[i], new MethodParameter(ctor, i));
|
||||
}
|
||||
return BeanUtils.instantiateClass(ctor, args);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@ import java.util.Optional;
|
|||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.codec.multipart.Part;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
|
@ -51,6 +48,7 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
public class RequestParamMapMethodArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
public RequestParamMapMethodArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
|
||||
super(adapterRegistry);
|
||||
}
|
||||
|
|
@ -70,18 +68,10 @@ public class RequestParamMapMethodArgumentResolver extends HandlerMethodArgument
|
|||
public Optional<Object> resolveArgumentValue(MethodParameter methodParameter,
|
||||
BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
ResolvableType paramType = ResolvableType.forType(methodParameter.getGenericParameterType());
|
||||
boolean isMultiValueMap = MultiValueMap.class.isAssignableFrom(paramType.getRawClass());
|
||||
|
||||
|
||||
if (paramType.getGeneric(1).getRawClass() == Part.class) {
|
||||
MultiValueMap<String, Part> requestParts = exchange.getMultipartData().subscribe().peek();
|
||||
Assert.notNull(requestParts, "Expected multipart data (if any) to be parsed.");
|
||||
return Optional.of(isMultiValueMap ? requestParts : requestParts.toSingleValueMap());
|
||||
}
|
||||
MultiValueMap<String, String> requestParams = exchange.getRequestParams().subscribe().peek();
|
||||
Assert.notNull(requestParams, "Expected form data (if any) to be parsed.");
|
||||
return Optional.of(isMultiValueMap ? requestParams : requestParams.toSingleValueMap());
|
||||
boolean isMultiValueMap = MultiValueMap.class.isAssignableFrom(methodParameter.getParameterType());
|
||||
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
|
||||
Object value = isMultiValueMap ? queryParams : queryParams.toSingleValueMap();
|
||||
return Optional.of(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,9 +25,6 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
|||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.http.codec.multipart.Part;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
|
|
@ -35,7 +32,8 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
import org.springframework.web.server.ServerWebInputException;
|
||||
|
||||
/**
|
||||
* Resolver for method arguments annotated with @{@link RequestParam}.
|
||||
* Resolver for method arguments annotated with @{@link RequestParam} from URI
|
||||
* query string parameters.
|
||||
*
|
||||
* <p>This resolver can also be created in default resolution mode in which
|
||||
* simple types (int, long, etc.) not annotated with @{@link RequestParam} are
|
||||
|
|
@ -103,7 +101,7 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueSyncAr
|
|||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
List<?> paramValues = parameter.getParameter().getType() == Part.class ? getMultipartData(exchange).get(name) : getRequestParams(exchange).get(name);
|
||||
List<String> paramValues = exchange.getRequest().getQueryParams().get(name);
|
||||
Object result = null;
|
||||
if (paramValues != null) {
|
||||
result = (paramValues.size() == 1 ? paramValues.get(0) : paramValues);
|
||||
|
|
@ -111,18 +109,6 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueSyncAr
|
|||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
private MultiValueMap<String, String> getRequestParams(ServerWebExchange exchange) {
|
||||
MultiValueMap<String, String> params = exchange.getRequestParams().subscribe().peek();
|
||||
Assert.notNull(params, "Expected form data (if any) to be parsed.");
|
||||
return params;
|
||||
}
|
||||
|
||||
private MultiValueMap<String, Part> getMultipartData(ServerWebExchange exchange) {
|
||||
MultiValueMap<String, Part> params = exchange.getMultipartData().subscribe().peek();
|
||||
Assert.notNull(params, "Expected multipart data (if any) to be parsed.");
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
String type = parameter.getNestedParameterType().getSimpleName();
|
||||
|
|
|
|||
|
|
@ -17,19 +17,13 @@
|
|||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.http.codec.multipart.Part;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
|
@ -39,48 +33,25 @@ import org.springframework.web.server.ServerWebInputException;
|
|||
* Resolver for method arguments annotated with @{@link RequestPart}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
* @see RequestParamMapMethodArgumentResolver
|
||||
*/
|
||||
public class RequestPartMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
private final boolean useDefaultResolution;
|
||||
|
||||
public class RequestPartMethodArgumentResolver extends AbstractNamedValueArgumentResolver {
|
||||
|
||||
/**
|
||||
* Class constructor with a default resolution mode flag.
|
||||
* @param factory 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 registry for checking reactive type wrappers
|
||||
* @param useDefaultResolution in default resolution mode a method argument
|
||||
* that is a simple type, as defined in {@link BeanUtils#isSimpleProperty},
|
||||
* is treated as a request parameter even if it isn't annotated, the
|
||||
* request parameter name is derived from the method parameter name.
|
||||
*/
|
||||
public RequestPartMethodArgumentResolver(
|
||||
ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry, boolean useDefaultResolution) {
|
||||
|
||||
super(factory, registry);
|
||||
this.useDefaultResolution = useDefaultResolution;
|
||||
public RequestPartMethodArgumentResolver(ReactiveAdapterRegistry registry) {
|
||||
super(null, registry);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter param) {
|
||||
if (checkAnnotatedParamNoReactiveWrapper(param, RequestPart.class, this::singleParam)) {
|
||||
return true;
|
||||
}
|
||||
else if (this.useDefaultResolution) {
|
||||
return checkParameterTypeNoReactiveWrapper(param, BeanUtils::isSimpleProperty) ||
|
||||
BeanUtils.isSimpleProperty(param.nestedIfOptional().getNestedParameterType());
|
||||
}
|
||||
return false;
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(RequestPart.class);
|
||||
}
|
||||
|
||||
private boolean singleParam(RequestPart requestParam, Class<?> type) {
|
||||
return !Map.class.isAssignableFrom(type) || StringUtils.hasText(requestParam.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
|
|
@ -89,28 +60,21 @@ public class RequestPartMethodArgumentResolver extends AbstractNamedValueSyncArg
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
List<?> paramValues = getMultipartData(exchange).get(name);
|
||||
Object result = null;
|
||||
if (paramValues != null) {
|
||||
result = (paramValues.size() == 1 ? paramValues.get(0) : paramValues);
|
||||
}
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
private MultiValueMap<String, Part> getMultipartData(ServerWebExchange exchange) {
|
||||
MultiValueMap<String, Part> params = exchange.getMultipartData().subscribe().peek();
|
||||
Assert.notNull(params, "Expected multipart data (if any) to be parsed.");
|
||||
return params;
|
||||
protected Mono<Object> resolveName(String name, MethodParameter param, ServerWebExchange exchange) {
|
||||
return exchange.getMultipartData().flatMap(allParts -> {
|
||||
List<Part> parts = allParts.get(name);
|
||||
if (CollectionUtils.isEmpty(parts)) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return Mono.just(parts.size() == 1 ? parts.get(0) : parts);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
String type = parameter.getNestedParameterType().getSimpleName();
|
||||
protected void handleMissingValue(String name, MethodParameter param, ServerWebExchange exchange) {
|
||||
String type = param.getNestedParameterType().getSimpleName();
|
||||
String reason = "Required " + type + " parameter '" + name + "' is not present";
|
||||
throw new ServerWebInputException(reason, parameter);
|
||||
throw new ServerWebInputException(reason, param);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -20,8 +20,6 @@ import java.util.Collection;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
|
|
@ -50,17 +48,13 @@ public class ParamsRequestConditionTests {
|
|||
@Test
|
||||
public void paramPresent() throws Exception {
|
||||
ParamsRequestCondition condition = new ParamsRequestCondition("foo");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(get("/path?foo=").toExchange()));
|
||||
assertNotNull(condition.getMatchingCondition(postForm("foo=")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramPresentNoMatch() throws Exception {
|
||||
ParamsRequestCondition condition = new ParamsRequestCondition("foo");
|
||||
|
||||
assertNull(condition.getMatchingCondition(get("/path?bar=").toExchange()));
|
||||
assertNull(condition.getMatchingCondition(postForm("bar=")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -72,17 +66,13 @@ public class ParamsRequestConditionTests {
|
|||
@Test
|
||||
public void paramValueMatch() throws Exception {
|
||||
ParamsRequestCondition condition = new ParamsRequestCondition("foo=bar");
|
||||
|
||||
assertNotNull(condition.getMatchingCondition(get("/path?foo=bar").toExchange()));
|
||||
assertNotNull(condition.getMatchingCondition(postForm("foo=bar")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramValueNoMatch() throws Exception {
|
||||
ParamsRequestCondition condition = new ParamsRequestCondition("foo=bar");
|
||||
|
||||
assertNull(condition.getMatchingCondition(get("/path?foo=bazz").toExchange()));
|
||||
assertNull(condition.getMatchingCondition(postForm("foo=bazz")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -109,12 +99,4 @@ public class ParamsRequestConditionTests {
|
|||
assertEquals(2, conditions.size());
|
||||
}
|
||||
|
||||
|
||||
private static MockServerWebExchange postForm(String formData) {
|
||||
return MockServerHttpRequest.post("/")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
.body(formData)
|
||||
.toExchange();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,8 +91,8 @@ public class ControllerMethodResolverTests {
|
|||
|
||||
AtomicInteger index = new AtomicInteger(-1);
|
||||
assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestPartMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestParamMapMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestPartMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(PathVariableMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(PathVariableMapMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestBodyArgumentResolver.class, next(resolvers, index).getClass());
|
||||
|
|
@ -130,8 +130,8 @@ public class ControllerMethodResolverTests {
|
|||
|
||||
AtomicInteger index = new AtomicInteger(-1);
|
||||
assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestPartMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestParamMapMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestPartMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(PathVariableMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(PathVariableMapMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(ModelAttributeMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
|
|
@ -167,7 +167,6 @@ public class ControllerMethodResolverTests {
|
|||
|
||||
AtomicInteger index = new AtomicInteger(-1);
|
||||
assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestPartMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestParamMapMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(PathVariableMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(PathVariableMapMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
|
|
@ -198,8 +197,8 @@ public class ControllerMethodResolverTests {
|
|||
|
||||
AtomicInteger index = new AtomicInteger(-1);
|
||||
assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestPartMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestParamMapMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestPartMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(PathVariableMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(PathVariableMapMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
assertEquals(RequestHeaderMethodArgumentResolver.class, next(resolvers, index).getClass());
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
|
@ -37,7 +35,6 @@ import org.springframework.http.server.reactive.HttpHandler;
|
|||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
|
|
@ -51,10 +48,9 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
public class MultipartIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext wac;
|
||||
|
||||
private WebClient webClient;
|
||||
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
|
|
@ -65,26 +61,10 @@ public class MultipartIntegrationTests extends AbstractHttpHandlerIntegrationTes
|
|||
|
||||
@Override
|
||||
protected HttpHandler createHttpHandler() {
|
||||
this.wac = new AnnotationConfigApplicationContext();
|
||||
this.wac.register(TestConfiguration.class);
|
||||
this.wac.refresh();
|
||||
|
||||
return WebHttpHandlerBuilder.webHandler(new DispatcherHandler(this.wac)).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void map() {
|
||||
test("/map");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiValueMap() {
|
||||
test("/multivaluemap");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partParam() {
|
||||
test("/partparam");
|
||||
AnnotationConfigApplicationContext wac = new AnnotationConfigApplicationContext();
|
||||
wac.register(TestConfiguration.class);
|
||||
wac.refresh();
|
||||
return WebHttpHandlerBuilder.webHandler(new DispatcherHandler(wac)).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -122,26 +102,6 @@ public class MultipartIntegrationTests extends AbstractHttpHandlerIntegrationTes
|
|||
@SuppressWarnings("unused")
|
||||
static class MultipartController {
|
||||
|
||||
@PostMapping("/map")
|
||||
void map(@RequestParam Map<String, Part> parts) {
|
||||
assertEquals(2, parts.size());
|
||||
assertEquals("foo.txt", parts.get("fooPart").getFilename().get());
|
||||
assertEquals("bar", parts.get("barPart").getContentAsString().block());
|
||||
}
|
||||
|
||||
@PostMapping("/multivaluemap")
|
||||
void multiValueMap(@RequestParam MultiValueMap<String, Part> parts) {
|
||||
Map<String, Part> map = parts.toSingleValueMap();
|
||||
assertEquals(2, map.size());
|
||||
assertEquals("foo.txt", map.get("fooPart").getFilename().get());
|
||||
assertEquals("bar", map.get("barPart").getContentAsString().block());
|
||||
}
|
||||
|
||||
@PostMapping("/partparam")
|
||||
void partParam(@RequestParam Part fooPart) {
|
||||
assertEquals("foo.txt", fooPart.getFilename().get());
|
||||
}
|
||||
|
||||
@PostMapping("/part")
|
||||
void part(@RequestPart Part fooPart) {
|
||||
assertEquals("foo.txt", fooPart.getFilename().get());
|
||||
|
|
|
|||
|
|
@ -36,8 +36,6 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED;
|
||||
import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.post;
|
||||
import static org.springframework.web.method.MvcAnnotationPredicates.requestParam;
|
||||
|
||||
/**
|
||||
|
|
@ -46,7 +44,8 @@ import static org.springframework.web.method.MvcAnnotationPredicates.requestPara
|
|||
*/
|
||||
public class RequestParamMapMethodArgumentResolverTests {
|
||||
|
||||
private RequestParamMapMethodArgumentResolver resolver = new RequestParamMapMethodArgumentResolver(new ReactiveAdapterRegistry());
|
||||
private final RequestParamMapMethodArgumentResolver resolver =
|
||||
new RequestParamMapMethodArgumentResolver(new ReactiveAdapterRegistry());
|
||||
|
||||
private ResolvableMethod testMethod = ResolvableMethod.on(getClass()).named("handle").build();
|
||||
|
||||
|
|
@ -85,15 +84,6 @@ public class RequestParamMapMethodArgumentResolverTests {
|
|||
assertEquals(Collections.singletonMap("foo", "bar"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMapArgumentWithFormData() throws Exception {
|
||||
MethodParameter param = this.testMethod.annot(requestParam().name("")).arg(Map.class);
|
||||
ServerWebExchange exchange = post("/").contentType(APPLICATION_FORM_URLENCODED).body("foo=bar").toExchange();
|
||||
Object result= resolve(param, exchange);
|
||||
assertTrue(result instanceof Map);
|
||||
assertEquals(Collections.singletonMap("foo", "bar"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMultiValueMapArgument() throws Exception {
|
||||
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import reactor.test.StepVerifier;
|
|||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerWebExchange;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
|
@ -139,18 +138,6 @@ public class RequestParamMethodArgumentResolverTests {
|
|||
assertEquals("foo", resolve(param, MockServerHttpRequest.get("/path?name=foo").toExchange()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveWithFormData() throws Exception {
|
||||
|
||||
ServerWebExchange exchange = MockServerHttpRequest.post("/path")
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
.body("name=foo")
|
||||
.toExchange();
|
||||
|
||||
MethodParameter param = this.testMethod.annot(requestParam().notRequired("bar")).arg(String.class);
|
||||
assertEquals("foo", resolve(param, exchange));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveStringArray() throws Exception {
|
||||
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(String[].class);
|
||||
|
|
|
|||
Loading…
Reference in New Issue