Support for reactive controller @InitBinder methods
Issue: SPR-14543
This commit is contained in:
parent
0b76b6d7e9
commit
3da0295c12
|
@ -15,11 +15,7 @@
|
|||
*/
|
||||
package org.springframework.web.reactive.result.method;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.validation.support.BindingAwareModelMap;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
|
@ -27,6 +23,7 @@ import org.springframework.web.bind.WebExchangeDataBinder;
|
|||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
|
||||
/**
|
||||
* A context for binding requests to method arguments that provides access to
|
||||
* the default model, data binding, validation, and type conversion.
|
||||
|
@ -34,7 +31,7 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class BindingContext implements TypeConverter {
|
||||
public class BindingContext {
|
||||
|
||||
private final ModelMap model = new BindingAwareModelMap();
|
||||
|
||||
|
@ -68,46 +65,50 @@ public class BindingContext implements TypeConverter {
|
|||
return this.model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link WebExchangeDataBinder} for the given object.
|
||||
* @param exchange the current exchange
|
||||
* @param target the object to create a data binder for, or {@code null} if
|
||||
* creating a binder for a simple type
|
||||
* @param objectName the name of the target object
|
||||
* @return a Mono for the created {@link WebDataBinder} instance
|
||||
*/
|
||||
public WebExchangeDataBinder createBinder(ServerWebExchange exchange, Object target,
|
||||
String objectName) {
|
||||
|
||||
WebExchangeDataBinder dataBinder = createBinderInstance(target, objectName);
|
||||
/**
|
||||
* Create a {@link WebExchangeDataBinder} for applying data binding, type
|
||||
* conversion, and validation on the given "target" object.
|
||||
*
|
||||
* @param exchange the current exchange
|
||||
* @param target the object to create a data binder for
|
||||
* @param name the name of the target object
|
||||
* @return the {@link WebDataBinder} instance
|
||||
*/
|
||||
public WebExchangeDataBinder createDataBinder(ServerWebExchange exchange, Object target, String name) {
|
||||
WebExchangeDataBinder dataBinder = createBinderInstance(target, name);
|
||||
if (this.initializer != null) {
|
||||
this.initializer.initBinder(dataBinder);
|
||||
}
|
||||
return initBinder(dataBinder, exchange);
|
||||
return initDataBinder(dataBinder, exchange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link WebExchangeDataBinder} without a "target" object, i.e.
|
||||
* for applying type conversion to simple types.
|
||||
*
|
||||
* @param exchange the current exchange
|
||||
* @param name the name of the target object
|
||||
* @return a Mono for the created {@link WebDataBinder} instance
|
||||
*/
|
||||
public WebExchangeDataBinder createDataBinder(ServerWebExchange exchange, String name) {
|
||||
return createDataBinder(exchange, null, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the data binder instance.
|
||||
*/
|
||||
protected WebExchangeDataBinder createBinderInstance(Object target, String objectName) {
|
||||
return new WebExchangeDataBinder(target, objectName);
|
||||
}
|
||||
|
||||
protected WebExchangeDataBinder initBinder(WebExchangeDataBinder binder, ServerWebExchange exchange) {
|
||||
/**
|
||||
* Initialize the data binder instance for the given exchange.
|
||||
*/
|
||||
protected WebExchangeDataBinder initDataBinder(WebExchangeDataBinder binder,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
return binder;
|
||||
}
|
||||
|
||||
public <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException {
|
||||
return this.simpleValueTypeConverter.convertIfNecessary(value, requiredType);
|
||||
}
|
||||
|
||||
public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
|
||||
throws TypeMismatchException {
|
||||
|
||||
return this.simpleValueTypeConverter.convertIfNecessary(value, requiredType, methodParam);
|
||||
}
|
||||
|
||||
public <T> T convertIfNecessary(Object value, Class<T> requiredType, Field field)
|
||||
throws TypeMismatchException {
|
||||
|
||||
return this.simpleValueTypeConverter.convertIfNecessary(value, requiredType, field);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,11 +67,24 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
|||
}
|
||||
|
||||
|
||||
public void setHandlerMethodArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
/**
|
||||
* Set {@link HandlerMethodArgumentResolver}s to use to use for resolving
|
||||
* method argument values.
|
||||
*/
|
||||
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
this.resolvers.clear();
|
||||
this.resolvers.addAll(resolvers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ParameterNameDiscoverer for resolving parameter names when needed
|
||||
* (e.g. default request attribute name).
|
||||
* <p>Default is a {@link DefaultParameterNameDiscoverer}.
|
||||
*/
|
||||
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Method getBridgedMethod() {
|
||||
return super.getBridgedMethod();
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.springframework.web.reactive.result.method;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -48,11 +49,11 @@ public class SyncInvocableHandlerMethod extends InvocableHandlerMethod {
|
|||
* all resolvers are {@link SyncHandlerMethodArgumentResolver}.
|
||||
*/
|
||||
@Override
|
||||
public void setHandlerMethodArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
public void setArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
resolvers.forEach(resolver ->
|
||||
Assert.isInstanceOf(SyncHandlerMethodArgumentResolver.class, resolver,
|
||||
"Expected sync argument resolver: " + resolver.getClass().getName()));
|
||||
super.setHandlerMethodArgumentResolvers(resolvers);
|
||||
super.setArgumentResolvers(resolvers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -213,7 +213,7 @@ public abstract class AbstractMessageReaderArgumentResolver {
|
|||
MethodParameter param, BindingContext binding, ServerWebExchange exchange) {
|
||||
|
||||
String name = Conventions.getVariableNameForParameter(param);
|
||||
WebExchangeDataBinder binder = binding.createBinder(exchange, target, name);
|
||||
WebExchangeDataBinder binder = binding.createDataBinder(exchange, target, name);
|
||||
binder.validate(validationHints);
|
||||
if (binder.getBindingResult().hasErrors()) {
|
||||
throw new ServerWebInputException("Validation failed", param);
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.springframework.beans.factory.config.BeanExpressionResolver;
|
|||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
|
@ -93,7 +94,7 @@ public abstract class AbstractNamedValueArgumentResolver implements HandlerMetho
|
|||
if ("".equals(arg) && namedValueInfo.defaultValue != null) {
|
||||
arg = resolveStringValue(namedValueInfo.defaultValue);
|
||||
}
|
||||
arg = applyConversion(arg, parameter, bindingContext);
|
||||
arg = applyConversion(arg, namedValueInfo, parameter, bindingContext, exchange);
|
||||
handleResolvedValue(arg, namedValueInfo.name, parameter, model, exchange);
|
||||
return arg;
|
||||
})
|
||||
|
@ -168,9 +169,12 @@ public abstract class AbstractNamedValueArgumentResolver implements HandlerMetho
|
|||
protected abstract Mono<Object> resolveName(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange);
|
||||
|
||||
private Object applyConversion(Object value, MethodParameter parameter, BindingContext bindingContext) {
|
||||
private Object applyConversion(Object value, NamedValueInfo namedValueInfo, MethodParameter parameter,
|
||||
BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
WebDataBinder binder = bindingContext.createDataBinder(exchange, namedValueInfo.name);
|
||||
try {
|
||||
value = bindingContext.convertIfNecessary(value, parameter.getParameterType(), parameter);
|
||||
value = binder.convertIfNecessary(value, parameter.getParameterType(), parameter);
|
||||
}
|
||||
catch (ConversionNotSupportedException ex) {
|
||||
throw new ServerErrorException("Conversion not supported.", parameter, ex);
|
||||
|
@ -193,7 +197,7 @@ public abstract class AbstractNamedValueArgumentResolver implements HandlerMetho
|
|||
handleMissingValue(namedValueInfo.name, parameter, exchange);
|
||||
}
|
||||
value = handleNullValue(namedValueInfo.name, value, parameter.getNestedParameterType());
|
||||
value = applyConversion(value, parameter, bindingContext);
|
||||
value = applyConversion(value, namedValueInfo, parameter, bindingContext, exchange);
|
||||
handleResolvedValue(value, namedValueInfo.name, parameter, model, exchange);
|
||||
return Mono.justOrEmpty(value);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.WebExchangeDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.reactive.HandlerResult;
|
||||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* An extension of {@link BindingContext} that uses {@code @InitBinder} methods
|
||||
* to initialize a data binder instance.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class InitBinderBindingContext extends BindingContext {
|
||||
|
||||
private final List<SyncInvocableHandlerMethod> binderMethods;
|
||||
|
||||
/** BindingContext for @InitBinder method invocation */
|
||||
private final BindingContext bindingContext;
|
||||
|
||||
|
||||
public InitBinderBindingContext(WebBindingInitializer initializer,
|
||||
List<SyncInvocableHandlerMethod> binderMethods) {
|
||||
|
||||
super(initializer);
|
||||
this.binderMethods = binderMethods;
|
||||
this.bindingContext = new BindingContext(initializer);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected WebExchangeDataBinder initDataBinder(WebExchangeDataBinder binder,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
this.binderMethods.stream()
|
||||
.filter(method -> isBinderMethodApplicable(method, binder))
|
||||
.forEach(method -> invokeInitBinderMethod(binder, exchange, method));
|
||||
|
||||
return binder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given {@code @InitBinder} method should be used to initialize
|
||||
* the given WebDataBinder instance. By default we check the attributes
|
||||
* names of the annotation, if present.
|
||||
*/
|
||||
protected boolean isBinderMethodApplicable(HandlerMethod binderMethod, WebDataBinder binder) {
|
||||
InitBinder annot = binderMethod.getMethodAnnotation(InitBinder.class);
|
||||
Collection<String> names = Arrays.asList(annot.value());
|
||||
return (names.size() == 0 || names.contains(binder.getObjectName()));
|
||||
}
|
||||
|
||||
private void invokeInitBinderMethod(WebExchangeDataBinder binder, ServerWebExchange exchange,
|
||||
SyncInvocableHandlerMethod method) {
|
||||
|
||||
HandlerResult result = method.invokeForHandlerResult(exchange, this.bindingContext, binder);
|
||||
if (result.getReturnValue().isPresent()) {
|
||||
throw new IllegalStateException("@InitBinder methods should return void: " + method);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,7 @@ import java.lang.reflect.Method;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
@ -31,12 +32,16 @@ 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.MethodIntrospector;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.codec.ByteArrayDecoder;
|
||||
import org.springframework.core.codec.ByteBufferDecoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.http.codec.DecoderHttpMessageReader;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
|
||||
|
@ -45,6 +50,8 @@ import org.springframework.web.reactive.HandlerResult;
|
|||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.InvocableHandlerMethod;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
|
@ -57,6 +64,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
|
||||
private static final Log logger = LogFactory.getLog(RequestMappingHandlerAdapter.class);
|
||||
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders = new ArrayList<>(10);
|
||||
|
||||
private WebBindingInitializer webBindingInitializer;
|
||||
|
@ -67,8 +75,15 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
|
||||
private List<HandlerMethodArgumentResolver> argumentResolvers;
|
||||
|
||||
private List<SyncHandlerMethodArgumentResolver> customInitBinderArgumentResolvers;
|
||||
|
||||
private List<SyncHandlerMethodArgumentResolver> initBinderArgumentResolvers;
|
||||
|
||||
private ConfigurableBeanFactory beanFactory;
|
||||
|
||||
|
||||
private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64);
|
||||
|
||||
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
|
||||
new ConcurrentHashMap<>(64);
|
||||
|
||||
|
@ -119,10 +134,10 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* Provide custom argument resolvers without overriding the built-in ones.
|
||||
* Configure custom argument resolvers without overriding the built-in ones.
|
||||
*/
|
||||
public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
this.customArgumentResolvers = argumentResolvers;
|
||||
public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
|
||||
this.customArgumentResolvers = resolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -147,6 +162,39 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
return this.argumentResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure custom argument resolvers for {@code @InitBinder} methods.
|
||||
*/
|
||||
public void setCustomInitBinderArgumentResolvers(List<SyncHandlerMethodArgumentResolver> resolvers) {
|
||||
this.customInitBinderArgumentResolvers = resolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the custom {@code @InitBinder} argument resolvers.
|
||||
*/
|
||||
public List<SyncHandlerMethodArgumentResolver> getCustomInitBinderArgumentResolvers() {
|
||||
return this.customInitBinderArgumentResolvers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the supported argument types in {@code @InitBinder} methods.
|
||||
*/
|
||||
public void setInitBinderArgumentResolvers(List<SyncHandlerMethodArgumentResolver> resolvers) {
|
||||
this.initBinderArgumentResolvers = null;
|
||||
if (resolvers != null) {
|
||||
this.initBinderArgumentResolvers = new ArrayList<>();
|
||||
this.initBinderArgumentResolvers.addAll(resolvers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured argument resolvers for {@code @InitBinder} methods.
|
||||
*/
|
||||
public List<SyncHandlerMethodArgumentResolver> getInitBinderArgumentResolvers() {
|
||||
return this.initBinderArgumentResolvers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A {@link ConfigurableBeanFactory} is expected for resolving expressions
|
||||
* in method argument default values.
|
||||
|
@ -166,21 +214,22 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.argumentResolvers == null) {
|
||||
this.argumentResolvers = initArgumentResolvers();
|
||||
this.argumentResolvers = getDefaultArgumentResolvers();
|
||||
}
|
||||
if (this.initBinderArgumentResolvers == null) {
|
||||
this.initBinderArgumentResolvers = getDefaultInitBinderArgumentResolvers();
|
||||
}
|
||||
}
|
||||
|
||||
protected List<HandlerMethodArgumentResolver> initArgumentResolvers() {
|
||||
protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
|
||||
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
|
||||
|
||||
ReactiveAdapterRegistry adapterRegistry = getReactiveAdapterRegistry();
|
||||
|
||||
// Annotation-based argument resolution
|
||||
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
|
||||
resolvers.add(new RequestParamMapMethodArgumentResolver());
|
||||
resolvers.add(new PathVariableMethodArgumentResolver(getBeanFactory()));
|
||||
resolvers.add(new PathVariableMapMethodArgumentResolver());
|
||||
resolvers.add(new RequestBodyArgumentResolver(getMessageReaders(), adapterRegistry));
|
||||
resolvers.add(new RequestBodyArgumentResolver(getMessageReaders(), getReactiveAdapterRegistry()));
|
||||
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
|
||||
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
|
||||
resolvers.add(new CookieValueMethodArgumentResolver(getBeanFactory()));
|
||||
|
@ -189,7 +238,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
resolvers.add(new RequestAttributeMethodArgumentResolver(getBeanFactory()));
|
||||
|
||||
// Type-based argument resolution
|
||||
resolvers.add(new HttpEntityArgumentResolver(getMessageReaders(), adapterRegistry));
|
||||
resolvers.add(new HttpEntityArgumentResolver(getMessageReaders(), getReactiveAdapterRegistry()));
|
||||
resolvers.add(new ModelArgumentResolver());
|
||||
resolvers.add(new ServerWebExchangeArgumentResolver());
|
||||
resolvers.add(new WebSessionArgumentResolver());
|
||||
|
@ -204,6 +253,35 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
return resolvers;
|
||||
}
|
||||
|
||||
protected List<SyncHandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
|
||||
List<SyncHandlerMethodArgumentResolver> resolvers = new ArrayList<>();
|
||||
|
||||
// Annotation-based argument resolution
|
||||
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
|
||||
resolvers.add(new RequestParamMapMethodArgumentResolver());
|
||||
resolvers.add(new PathVariableMethodArgumentResolver(getBeanFactory()));
|
||||
resolvers.add(new PathVariableMapMethodArgumentResolver());
|
||||
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
|
||||
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
|
||||
resolvers.add(new CookieValueMethodArgumentResolver(getBeanFactory()));
|
||||
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
|
||||
resolvers.add(new RequestAttributeMethodArgumentResolver(getBeanFactory()));
|
||||
|
||||
// Type-based argument resolution
|
||||
resolvers.add(new ModelArgumentResolver());
|
||||
resolvers.add(new ServerWebExchangeArgumentResolver());
|
||||
|
||||
// Custom resolvers
|
||||
if (getCustomArgumentResolvers() != null) {
|
||||
resolvers.addAll(getCustomInitBinderArgumentResolvers());
|
||||
}
|
||||
|
||||
// Catch-all
|
||||
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
|
||||
return resolvers;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(Object handler) {
|
||||
return HandlerMethod.class.equals(handler.getClass());
|
||||
|
@ -213,8 +291,8 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod);
|
||||
invocable.setHandlerMethodArgumentResolvers(getArgumentResolvers());
|
||||
BindingContext bindingContext = new BindingContext(getWebBindingInitializer());
|
||||
invocable.setArgumentResolvers(getArgumentResolvers());
|
||||
BindingContext bindingContext = getBindingContext(handlerMethod);
|
||||
return invocable.invoke(exchange, bindingContext)
|
||||
.map(result -> result.setExceptionHandler(
|
||||
ex -> handleException(ex, handlerMethod, bindingContext, exchange)))
|
||||
|
@ -222,6 +300,24 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
ex, handlerMethod, bindingContext, exchange));
|
||||
}
|
||||
|
||||
private BindingContext getBindingContext(HandlerMethod handlerMethod) {
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
Set<Method> methods = this.initBinderCache.get(handlerType);
|
||||
if (methods == null) {
|
||||
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
|
||||
this.initBinderCache.put(handlerType, methods);
|
||||
}
|
||||
List<SyncInvocableHandlerMethod> initBinderMethods = new ArrayList<>();
|
||||
for (Method method : methods) {
|
||||
Object bean = handlerMethod.getBean();
|
||||
SyncInvocableHandlerMethod initBinderMethod = new SyncInvocableHandlerMethod(bean, method);
|
||||
initBinderMethod.setArgumentResolvers(new ArrayList<>(this.initBinderArgumentResolvers));
|
||||
initBinderMethods.add(initBinderMethod);
|
||||
}
|
||||
return new InitBinderBindingContext(getWebBindingInitializer(), initBinderMethods);
|
||||
}
|
||||
|
||||
|
||||
private Mono<HandlerResult> handleException(Throwable ex, HandlerMethod handlerMethod,
|
||||
BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
|
@ -231,7 +327,7 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invoking @ExceptionHandler method: " + invocable.getMethod());
|
||||
}
|
||||
invocable.setHandlerMethodArgumentResolvers(getArgumentResolvers());
|
||||
invocable.setArgumentResolvers(getArgumentResolvers());
|
||||
bindingContext.getModel().clear();
|
||||
return invocable.invoke(exchange, bindingContext, ex);
|
||||
}
|
||||
|
@ -259,4 +355,11 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
|||
return (method != null ? new InvocableHandlerMethod(handlerMethod.getBean(), method) : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MethodFilter that matches {@link InitBinder @InitBinder} methods.
|
||||
*/
|
||||
public static final ReflectionUtils.MethodFilter INIT_BINDER_METHODS =
|
||||
method -> AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
|
||||
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ public class InvocableHandlerMethodTests {
|
|||
HandlerMethodArgumentResolver resolver = mock(HandlerMethodArgumentResolver.class);
|
||||
when(resolver.supportsParameter(any())).thenReturn(true);
|
||||
when(resolver.resolveArgument(any(), any(), any())).thenReturn(resolvedValue);
|
||||
handlerMethod.setHandlerMethodArgumentResolvers(Collections.singletonList(resolver));
|
||||
handlerMethod.setArgumentResolvers(Collections.singletonList(resolver));
|
||||
}
|
||||
|
||||
private void assertHandlerResultValue(Mono<HandlerResult> mono, String expected) {
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.reactive.result.method.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.DefaultWebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for {@link InitBinderBindingContext}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class InitBinderBindingContextTests {
|
||||
|
||||
private final ConfigurableWebBindingInitializer bindingInitializer = new ConfigurableWebBindingInitializer();
|
||||
|
||||
private final List<SyncHandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
|
||||
|
||||
private final ServerWebExchange exchange = new DefaultServerWebExchange(
|
||||
new MockServerHttpRequest(), new MockServerHttpResponse(), new DefaultWebSessionManager());
|
||||
|
||||
|
||||
@Test
|
||||
public void createBinder() throws Exception {
|
||||
BindingContext context = createBindingContext("initBinder", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = context.createDataBinder(this.exchange, null, null);
|
||||
|
||||
assertNotNull(dataBinder.getDisallowedFields());
|
||||
assertEquals("id", dataBinder.getDisallowedFields()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderWithGlobalInitialization() throws Exception {
|
||||
ConversionService conversionService = new DefaultFormattingConversionService();
|
||||
bindingInitializer.setConversionService(conversionService);
|
||||
|
||||
BindingContext context = createBindingContext("initBinder", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = context.createDataBinder(this.exchange, null, null);
|
||||
|
||||
assertSame(conversionService, dataBinder.getConversionService());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderWithAttrName() throws Exception {
|
||||
BindingContext context = createBindingContext("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = context.createDataBinder(this.exchange, null, "foo");
|
||||
|
||||
assertNotNull(dataBinder.getDisallowedFields());
|
||||
assertEquals("id", dataBinder.getDisallowedFields()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderWithAttrNameNoMatch() throws Exception {
|
||||
BindingContext context = createBindingContext("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = context.createDataBinder(this.exchange, null, "invalidName");
|
||||
|
||||
assertNull(dataBinder.getDisallowedFields());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderNullAttrName() throws Exception {
|
||||
BindingContext context = createBindingContext("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = context.createDataBinder(this.exchange, null, null);
|
||||
|
||||
assertNull(dataBinder.getDisallowedFields());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void returnValueNotExpected() throws Exception {
|
||||
BindingContext context = createBindingContext("initBinderReturnValue", WebDataBinder.class);
|
||||
context.createDataBinder(this.exchange, null, "invalidName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderTypeConversion() throws Exception {
|
||||
this.exchange.getRequest().getQueryParams().add("requestParam", "22");
|
||||
this.argumentResolvers.add(new RequestParamMethodArgumentResolver(null, false));
|
||||
|
||||
BindingContext context = createBindingContext("initBinderTypeConversion", WebDataBinder.class, int.class);
|
||||
WebDataBinder dataBinder = context.createDataBinder(this.exchange, null, "foo");
|
||||
|
||||
assertNotNull(dataBinder.getDisallowedFields());
|
||||
assertEquals("requestParam-22", dataBinder.getDisallowedFields()[0]);
|
||||
}
|
||||
|
||||
|
||||
private BindingContext createBindingContext(String methodName, Class<?>... parameterTypes)
|
||||
throws Exception {
|
||||
|
||||
Object handler = new InitBinderHandler();
|
||||
Method method = handler.getClass().getMethod(methodName, parameterTypes);
|
||||
|
||||
SyncInvocableHandlerMethod handlerMethod = new SyncInvocableHandlerMethod(handler, method);
|
||||
handlerMethod.setArgumentResolvers(new ArrayList<>(this.argumentResolvers));
|
||||
handlerMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
||||
|
||||
return new InitBinderBindingContext(
|
||||
this.bindingInitializer, Collections.singletonList(handlerMethod));
|
||||
}
|
||||
|
||||
|
||||
private static class InitBinderHandler {
|
||||
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder dataBinder) {
|
||||
dataBinder.setDisallowedFields("id");
|
||||
}
|
||||
|
||||
@InitBinder(value="foo")
|
||||
public void initBinderWithAttributeName(WebDataBinder dataBinder) {
|
||||
dataBinder.setDisallowedFields("id");
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
public String initBinderReturnValue(WebDataBinder dataBinder) {
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
public void initBinderTypeConversion(WebDataBinder dataBinder, @RequestParam int requestParam) {
|
||||
dataBinder.setDisallowedFields("requestParam-" + requestParam);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.reactive.config.EnableWebReactive;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
/**
|
||||
* Data binding and type conversion related integration tests for
|
||||
* {@code @Controller}-annotated classes.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class RequestMappingDataBindingIntegrationTests extends AbstractRequestMappingIntegrationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext initApplicationContext() {
|
||||
AnnotationConfigApplicationContext wac = new AnnotationConfigApplicationContext();
|
||||
wac.register(WebConfig.class);
|
||||
wac.refresh();
|
||||
return wac;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void handleDateParam() throws Exception {
|
||||
assertEquals("Processed date!",
|
||||
performPost("/date-param?date=2016-10-31&date-pattern=YYYY-mm-dd",
|
||||
new HttpHeaders(), null, String.class).getBody());
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableWebReactive
|
||||
@ComponentScan(resourcePattern = "**/RequestMappingDataBindingIntegrationTests*.class")
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
static class WebConfig {
|
||||
}
|
||||
|
||||
@Controller
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestController {
|
||||
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder dataBinder, @RequestParam("date-pattern") String pattern) {
|
||||
CustomDateEditor dateEditor = new CustomDateEditor(new SimpleDateFormat(pattern), false);
|
||||
dataBinder.registerCustomEditor(Date.class, dateEditor);
|
||||
}
|
||||
|
||||
@PostMapping("/date-param")
|
||||
@ResponseBody
|
||||
public String handleDateParam(@RequestParam Date date) {
|
||||
return "Processed date!";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -39,12 +39,15 @@ public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
|
|||
|
||||
private final List<InvocableHandlerMethod> binderMethods;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
* @param binderMethods {@code @InitBinder} methods, or {@code null}
|
||||
* @param initializer for global data binder intialization
|
||||
*/
|
||||
public InitBinderDataBinderFactory(List<InvocableHandlerMethod> binderMethods, WebBindingInitializer initializer) {
|
||||
public InitBinderDataBinderFactory(List<InvocableHandlerMethod> binderMethods,
|
||||
WebBindingInitializer initializer) {
|
||||
|
||||
super(initializer);
|
||||
this.binderMethods = (binderMethods != null) ? binderMethods : new ArrayList<>();
|
||||
}
|
||||
|
@ -61,20 +64,20 @@ public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
|
|||
if (isBinderMethodApplicable(binderMethod, binder)) {
|
||||
Object returnValue = binderMethod.invokeForRequest(request, null, binder);
|
||||
if (returnValue != null) {
|
||||
throw new IllegalStateException("@InitBinder methods should return void: " + binderMethod);
|
||||
throw new IllegalStateException(
|
||||
"@InitBinder methods should return void: " + binderMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the given {@code @InitBinder} method should be
|
||||
* invoked to initialize the given WebDataBinder.
|
||||
* <p>The default implementation checks if target object name is included
|
||||
* in the attribute names specified in the {@code @InitBinder} annotation.
|
||||
* Whether the given {@code @InitBinder} method should be used to initialize
|
||||
* the given WebDataBinder instance. By default we check the attributes
|
||||
* names of the annotation, if present.
|
||||
*/
|
||||
protected boolean isBinderMethodApplicable(HandlerMethod initBinderMethod, WebDataBinder binder) {
|
||||
InitBinder annot = initBinderMethod.getMethodAnnotation(InitBinder.class);
|
||||
protected boolean isBinderMethodApplicable(HandlerMethod binderMethod, WebDataBinder binder) {
|
||||
InitBinder annot = binderMethod.getMethodAnnotation(InitBinder.class);
|
||||
Collection<String> names = Arrays.asList(annot.value());
|
||||
return (names.size() == 0 || names.contains(binder.getObjectName()));
|
||||
}
|
||||
|
|
|
@ -17,9 +17,8 @@
|
|||
package org.springframework.web.method.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
|
@ -37,7 +36,10 @@ import org.springframework.web.context.request.ServletWebRequest;
|
|||
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link InitBinderDataBinderFactory}.
|
||||
|
@ -46,23 +48,19 @@ import static org.junit.Assert.*;
|
|||
*/
|
||||
public class InitBinderDataBinderFactoryTests {
|
||||
|
||||
private ConfigurableWebBindingInitializer bindingInitializer;
|
||||
private final ConfigurableWebBindingInitializer bindingInitializer =
|
||||
new ConfigurableWebBindingInitializer();
|
||||
|
||||
private HandlerMethodArgumentResolverComposite argumentResolvers;
|
||||
private final HandlerMethodArgumentResolverComposite argumentResolvers =
|
||||
new HandlerMethodArgumentResolverComposite();
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
private final NativeWebRequest webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
bindingInitializer = new ConfigurableWebBindingInitializer();
|
||||
argumentResolvers = new HandlerMethodArgumentResolverComposite();
|
||||
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinder() throws Exception {
|
||||
WebDataBinderFactory factory = createBinderFactory("initBinder", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, null);
|
||||
WebDataBinderFactory factory = createFactory("initBinder", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(this.webRequest, null, null);
|
||||
|
||||
assertNotNull(dataBinder.getDisallowedFields());
|
||||
assertEquals("id", dataBinder.getDisallowedFields()[0]);
|
||||
|
@ -73,16 +71,16 @@ public class InitBinderDataBinderFactoryTests {
|
|||
ConversionService conversionService = new DefaultFormattingConversionService();
|
||||
bindingInitializer.setConversionService(conversionService);
|
||||
|
||||
WebDataBinderFactory factory = createBinderFactory("initBinder", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, null);
|
||||
WebDataBinderFactory factory = createFactory("initBinder", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(this.webRequest, null, null);
|
||||
|
||||
assertSame(conversionService, dataBinder.getConversionService());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderWithAttrName() throws Exception {
|
||||
WebDataBinderFactory factory = createBinderFactory("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, "foo");
|
||||
WebDataBinderFactory factory = createFactory("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(this.webRequest, null, "foo");
|
||||
|
||||
assertNotNull(dataBinder.getDisallowedFields());
|
||||
assertEquals("id", dataBinder.getDisallowedFields()[0]);
|
||||
|
@ -90,52 +88,54 @@ public class InitBinderDataBinderFactoryTests {
|
|||
|
||||
@Test
|
||||
public void createBinderWithAttrNameNoMatch() throws Exception {
|
||||
WebDataBinderFactory factory = createBinderFactory("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, "invalidName");
|
||||
WebDataBinderFactory factory = createFactory("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(this.webRequest, null, "invalidName");
|
||||
|
||||
assertNull(dataBinder.getDisallowedFields());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderNullAttrName() throws Exception {
|
||||
WebDataBinderFactory factory = createBinderFactory("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, null);
|
||||
WebDataBinderFactory factory = createFactory("initBinderWithAttributeName", WebDataBinder.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(this.webRequest, null, null);
|
||||
|
||||
assertNull(dataBinder.getDisallowedFields());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void returnValueNotExpected() throws Exception {
|
||||
WebDataBinderFactory factory = createBinderFactory("initBinderReturnValue", WebDataBinder.class);
|
||||
factory.createBinder(webRequest, null, "invalidName");
|
||||
WebDataBinderFactory factory = createFactory("initBinderReturnValue", WebDataBinder.class);
|
||||
factory.createBinder(this.webRequest, null, "invalidName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBinderTypeConversion() throws Exception {
|
||||
webRequest.getNativeRequest(MockHttpServletRequest.class).setParameter("requestParam", "22");
|
||||
argumentResolvers.addResolver(new RequestParamMethodArgumentResolver(null, false));
|
||||
this.webRequest.getNativeRequest(MockHttpServletRequest.class).setParameter("requestParam", "22");
|
||||
this.argumentResolvers.addResolver(new RequestParamMethodArgumentResolver(null, false));
|
||||
|
||||
WebDataBinderFactory factory = createBinderFactory("initBinderTypeConversion", WebDataBinder.class, int.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(webRequest, null, "foo");
|
||||
WebDataBinderFactory factory = createFactory("initBinderTypeConversion", WebDataBinder.class, int.class);
|
||||
WebDataBinder dataBinder = factory.createBinder(this.webRequest, null, "foo");
|
||||
|
||||
assertNotNull(dataBinder.getDisallowedFields());
|
||||
assertEquals("requestParam-22", dataBinder.getDisallowedFields()[0]);
|
||||
}
|
||||
|
||||
private WebDataBinderFactory createBinderFactory(String methodName, Class<?>... parameterTypes)
|
||||
private WebDataBinderFactory createFactory(String methodName, Class<?>... parameterTypes)
|
||||
throws Exception {
|
||||
|
||||
Object handler = new InitBinderHandler();
|
||||
Method method = handler.getClass().getMethod(methodName, parameterTypes);
|
||||
|
||||
InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(handler, method);
|
||||
handlerMethod.setHandlerMethodArgumentResolvers(argumentResolvers);
|
||||
handlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
|
||||
handlerMethod.setDataBinderFactory(new DefaultDataBinderFactory(null));
|
||||
handlerMethod.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
||||
|
||||
return new InitBinderDataBinderFactory(Arrays.asList(handlerMethod), bindingInitializer);
|
||||
return new InitBinderDataBinderFactory(
|
||||
Collections.singletonList(handlerMethod), this.bindingInitializer);
|
||||
}
|
||||
|
||||
|
||||
private static class InitBinderHandler {
|
||||
|
||||
@InitBinder
|
||||
|
|
Loading…
Reference in New Issue