@MatrixVariable resolvers for WebFlux
The information was already parsed and available in a request attribute but until now there were no argument resolvers to expose it. Issue: SPR-16005
This commit is contained in:
@@ -38,13 +38,16 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
public abstract class AbstractNamedValueSyncArgumentResolver extends AbstractNamedValueArgumentResolver
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
* @param factory a bean factory to use for resolving ${...}
|
||||
* placeholder and #{...} SpEL expressions in default values;
|
||||
* or {@code null} if default values are not expected to have expressions
|
||||
* @param registry for checking reactive type wrappers
|
||||
*/
|
||||
protected AbstractNamedValueSyncArgumentResolver(@Nullable ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry) {
|
||||
protected AbstractNamedValueSyncArgumentResolver(@Nullable ConfigurableBeanFactory factory,
|
||||
ReactiveAdapterRegistry registry) {
|
||||
|
||||
super(factory, registry);
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,8 @@ class ControllerMethodResolver {
|
||||
registrar.add(new RequestParamMapMethodArgumentResolver(reactiveRegistry));
|
||||
registrar.add(new PathVariableMethodArgumentResolver(beanFactory, reactiveRegistry));
|
||||
registrar.add(new PathVariableMapMethodArgumentResolver(reactiveRegistry));
|
||||
registrar.add(new MatrixVariableMethodArgumentResolver(beanFactory, reactiveRegistry));
|
||||
registrar.add(new MatrixVariableMapMethodArgumentResolver(reactiveRegistry));
|
||||
registrar.addIfRequestBody(readers -> new RequestBodyArgumentResolver(readers, reactiveRegistry));
|
||||
registrar.addIfRequestBody(readers -> new RequestPartMethodArgumentResolver(readers, reactiveRegistry));
|
||||
registrar.addIfModelAttribute(() -> new ModelAttributeMethodArgumentResolver(reactiveRegistry, false));
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.MatrixVariable;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* Resolves arguments of type {@link Map} annotated with {@link MatrixVariable
|
||||
* @MatrixVariable} where the annotation does not specify a name. In other words
|
||||
* the purpose of this resolver is to provide access to multiple matrix
|
||||
* variables, either all or associted with a specific path variable.
|
||||
*
|
||||
* <p>When a name is specified, an argument of type Map is considered to be an
|
||||
* single attribute with a Map value, and is resolved by
|
||||
* {@link MatrixVariableMethodArgumentResolver} instead.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0.1
|
||||
* @see MatrixVariableMethodArgumentResolver
|
||||
*/
|
||||
public class MatrixVariableMapMethodArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
public MatrixVariableMapMethodArgumentResolver(ReactiveAdapterRegistry registry) {
|
||||
super(registry);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return checkAnnotatedParamNoReactiveWrapper(parameter, MatrixVariable.class,
|
||||
(annot, type) -> (Map.class.isAssignableFrom(type) && !StringUtils.hasText(annot.name())));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
Map<String, MultiValueMap<String, String>> matrixVariables =
|
||||
exchange.getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE);
|
||||
|
||||
if (CollectionUtils.isEmpty(matrixVariables)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
|
||||
MatrixVariable annotation = parameter.getParameterAnnotation(MatrixVariable.class);
|
||||
Assert.state(annotation != null, "No MatrixVariable annotation");
|
||||
String pathVariable = annotation.pathVar();
|
||||
|
||||
if (!pathVariable.equals(ValueConstants.DEFAULT_NONE)) {
|
||||
MultiValueMap<String, String> mapForPathVariable = matrixVariables.get(pathVariable);
|
||||
if (mapForPathVariable == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
map.putAll(mapForPathVariable);
|
||||
}
|
||||
else {
|
||||
for (MultiValueMap<String, String> vars : matrixVariables.values()) {
|
||||
for (String name : vars.keySet()) {
|
||||
for (String value : vars.get(name)) {
|
||||
map.add(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (isSingleValueMap(parameter) ? map.toSingleValueMap() : map);
|
||||
}
|
||||
|
||||
private boolean isSingleValueMap(MethodParameter parameter) {
|
||||
if (!MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
|
||||
ResolvableType[] genericTypes = ResolvableType.forMethodParameter(parameter).getGenerics();
|
||||
if (genericTypes.length == 2) {
|
||||
Class<?> declaredClass = genericTypes[1].getRawClass();
|
||||
return (declaredClass == null || !List.class.isAssignableFrom(declaredClass));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.MatrixVariable;
|
||||
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;
|
||||
import org.springframework.web.server.ServerWebInputException;
|
||||
|
||||
/**
|
||||
* Resolves arguments annotated with {@link MatrixVariable @MatrixVariable}.
|
||||
*
|
||||
* <p>If the method parameter is of type {@link Map} it will by resolved by
|
||||
* {@link MatrixVariableMapMethodArgumentResolver} instead unless the annotation
|
||||
* specifies a name in which case it is considered to be a single attribute of
|
||||
* type map (vs multiple attributes collected in a map).
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0.1
|
||||
* @see MatrixVariableMapMethodArgumentResolver
|
||||
*/
|
||||
public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
|
||||
protected MatrixVariableMethodArgumentResolver(@Nullable ConfigurableBeanFactory factory,
|
||||
ReactiveAdapterRegistry registry) {
|
||||
|
||||
super(factory, registry);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return checkAnnotatedParamNoReactiveWrapper(parameter, MatrixVariable.class,
|
||||
(annot, type) -> !Map.class.isAssignableFrom(type) || StringUtils.hasText(annot.name()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
MatrixVariable ann = parameter.getParameterAnnotation(MatrixVariable.class);
|
||||
Assert.state(ann != null, "No MatrixVariable annotation");
|
||||
return new MatrixVariableNamedValueInfo(ann);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Object resolveNamedValue(String name, MethodParameter param, ServerWebExchange exchange) {
|
||||
|
||||
Map<String, MultiValueMap<String, String>> pathParameters =
|
||||
exchange.getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE);
|
||||
|
||||
if (CollectionUtils.isEmpty(pathParameters)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MatrixVariable ann = param.getParameterAnnotation(MatrixVariable.class);
|
||||
Assert.state(ann != null, "No MatrixVariable annotation");
|
||||
String pathVar = ann.pathVar();
|
||||
List<String> paramValues = null;
|
||||
|
||||
if (!pathVar.equals(ValueConstants.DEFAULT_NONE)) {
|
||||
if (pathParameters.containsKey(pathVar)) {
|
||||
paramValues = pathParameters.get(pathVar).get(name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
boolean found = false;
|
||||
paramValues = new ArrayList<>();
|
||||
for (MultiValueMap<String, String> params : pathParameters.values()) {
|
||||
if (params.containsKey(name)) {
|
||||
if (found) {
|
||||
String paramType = param.getNestedParameterType().getName();
|
||||
throw new ServerErrorException(
|
||||
"Found more than one match for URI path parameter '" + name +
|
||||
"' for parameter type [" + paramType + "]. Use 'pathVar' attribute to disambiguate.");
|
||||
}
|
||||
paramValues.addAll(params.get(name));
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(paramValues)) {
|
||||
return null;
|
||||
}
|
||||
else if (paramValues.size() == 1) {
|
||||
return paramValues.get(0);
|
||||
}
|
||||
else {
|
||||
return paramValues;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String name, MethodParameter parameter) throws ServerWebInputException {
|
||||
throw new ServerWebInputException("Missing matrix variable '" + name +
|
||||
"' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
|
||||
}
|
||||
|
||||
|
||||
private static class MatrixVariableNamedValueInfo extends NamedValueInfo {
|
||||
|
||||
private MatrixVariableNamedValueInfo(MatrixVariable annotation) {
|
||||
super(annotation.name(), annotation.required(), annotation.defaultValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user