Support for populating model attributes through data class constructors
Includes a new overloaded ModelAndView constructor with an HttpStatus argument, as well as a HandlerMethodArgumentResolverSupport refactoring (revised checkParameterType signature, actually implementing the HandlerMethodArgumentResolver interface). Issue: SPR-15199
This commit is contained in:
@@ -16,15 +16,20 @@
|
||||
|
||||
package org.springframework.web.method.annotation;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -52,10 +57,13 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
* attribute with or without the presence of an {@code @ModelAttribute}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
|
||||
|
||||
private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final boolean annotationNotRequired;
|
||||
@@ -65,7 +73,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
* Class constructor.
|
||||
* @param annotationNotRequired if "true", non-simple method arguments and
|
||||
* return values are considered model attributes with or without a
|
||||
* {@code @ModelAttribute} annotation.
|
||||
* {@code @ModelAttribute} annotation
|
||||
*/
|
||||
public ModelAttributeMethodProcessor(boolean annotationNotRequired) {
|
||||
this.annotationNotRequired = annotationNotRequired;
|
||||
@@ -89,8 +97,8 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
* with request values via data binding and optionally validated
|
||||
* if {@code @java.validation.Valid} is present on the argument.
|
||||
* @throws BindException if data binding and validation result in an error
|
||||
* and the next method parameter is not of type {@link Errors}.
|
||||
* @throws Exception if WebDataBinder initialization fails.
|
||||
* and the next method parameter is not of type {@link Errors}
|
||||
* @throws Exception if WebDataBinder initialization fails
|
||||
*/
|
||||
@Override
|
||||
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
@@ -123,22 +131,54 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
mavContainer.removeAttributes(bindingResultModel);
|
||||
mavContainer.addAllAttributes(bindingResultModel);
|
||||
|
||||
return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
|
||||
return (parameter.getParameterType().isInstance(attribute) ? attribute :
|
||||
binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point to create the model attribute if not found in the model.
|
||||
* The default implementation uses the default constructor.
|
||||
* Extension point to create the model attribute if not found in the model,
|
||||
* with subsequent parameter binding through bean properties (unless suppressed).
|
||||
* <p>The default implementation uses the unique public no-arg constructor, if any,
|
||||
* which may have arguments: It understands the JavaBeans {@link ConstructorProperties}
|
||||
* annotation as well as runtime-retained parameter names in the bytecode,
|
||||
* associating request parameters with constructor arguments by name. If no such
|
||||
* constructor is found, the default constructor will be used (even if not public),
|
||||
* assuming subsequent bean property bindings through setter methods.
|
||||
* @param attributeName the name of the attribute (never {@code null})
|
||||
* @param methodParam the method parameter
|
||||
* @param parameter the method parameter declaration
|
||||
* @param binderFactory for creating WebDataBinder instance
|
||||
* @param request the current request
|
||||
* @param webRequest the current request
|
||||
* @return the created model attribute (never {@code null})
|
||||
*/
|
||||
protected Object createAttribute(String attributeName, MethodParameter methodParam,
|
||||
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
|
||||
protected Object createAttribute(String attributeName, MethodParameter parameter,
|
||||
WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
return BeanUtils.instantiateClass(methodParam.getParameterType());
|
||||
Constructor<?>[] ctors = parameter.getParameterType().getConstructors();
|
||||
if (ctors.length != 1) {
|
||||
// No standard data class or standard JavaBeans arrangement ->
|
||||
// defensively go with default constructor, expecting regular bean property bindings.
|
||||
return BeanUtils.instantiateClass(parameter.getParameterType());
|
||||
}
|
||||
Constructor<?> ctor = ctors[0];
|
||||
if (ctor.getParameterCount() == 0) {
|
||||
// A single default constructor -> clearly a standard JavaBeans arrangement.
|
||||
return BeanUtils.instantiateClass(ctor);
|
||||
}
|
||||
|
||||
// A single data class constructor -> resolve constructor arguments from request parameters.
|
||||
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);
|
||||
Class<?>[] paramTypes = ctor.getParameterTypes();
|
||||
Assert.state(paramNames.length == paramTypes.length,
|
||||
() -> "Invalid number of parameter names: " + paramNames.length + " for constructor " + ctor);
|
||||
Object[] args = new Object[paramTypes.length];
|
||||
WebDataBinder binder = binderFactory.createBinder(webRequest, null, attributeName);
|
||||
for (int i = 0; i < paramNames.length; i++) {
|
||||
args[i] = binder.convertIfNecessary(
|
||||
webRequest.getParameterValues(paramNames[i]), paramTypes[i], new MethodParameter(ctor, i));
|
||||
}
|
||||
return BeanUtils.instantiateClass(ctor, args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,10 +196,10 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
* Spring's {@link org.springframework.validation.annotation.Validated},
|
||||
* and custom annotations whose name starts with "Valid".
|
||||
* @param binder the DataBinder to be used
|
||||
* @param methodParam the method parameter
|
||||
* @param parameter the method parameter declaration
|
||||
*/
|
||||
protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {
|
||||
Annotation[] annotations = methodParam.getParameterAnnotations();
|
||||
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
|
||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||
for (Annotation ann : annotations) {
|
||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
||||
@@ -174,12 +214,12 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
|
||||
/**
|
||||
* Whether to raise a fatal bind exception on validation errors.
|
||||
* @param binder the data binder used to perform data binding
|
||||
* @param methodParam the method argument
|
||||
* @param parameter the method parameter declaration
|
||||
* @return {@code true} if the next method argument is not of type {@link Errors}
|
||||
*/
|
||||
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
|
||||
int i = methodParam.getParameterIndex();
|
||||
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
|
||||
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
|
||||
int i = parameter.getParameterIndex();
|
||||
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
|
||||
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
return !hasBindingResult;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -70,7 +70,7 @@ public class ModelAttributeMethodProcessorTests {
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
public void setup() throws Exception {
|
||||
this.request = new ServletWebRequest(new MockHttpServletRequest());
|
||||
this.container = new ModelAndViewContainer();
|
||||
this.processor = new ModelAttributeMethodProcessor(false);
|
||||
@@ -145,7 +145,7 @@ public class ModelAttributeMethodProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resovleArgumentViaDefaultConstructor() throws Exception {
|
||||
public void resolveArgumentViaDefaultConstructor() throws Exception {
|
||||
WebDataBinder dataBinder = new WebRequestDataBinder(null);
|
||||
WebDataBinderFactory factory = mock(WebDataBinderFactory.class);
|
||||
given(factory.createBinder(any(), notNull(), eq("attrName"))).willReturn(dataBinder);
|
||||
|
||||
@@ -44,7 +44,7 @@ public interface HandlerMethodArgumentResolver {
|
||||
* @param exchange the current exchange
|
||||
* @return {@code Mono} for the argument value, possibly empty
|
||||
*/
|
||||
Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange);
|
||||
Mono<Object> resolveArgument(
|
||||
MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange);
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.method;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -25,14 +26,14 @@ import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for {@link HandlerMethodArgumentResolver} implementations with
|
||||
* access to a {@code ReactiveAdapterRegistry} and methods to check for
|
||||
* method parameter support.
|
||||
* Base class for {@link HandlerMethodArgumentResolver} implementations with access to a
|
||||
* {@code ReactiveAdapterRegistry} and methods to check for method parameter support.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class HandlerMethodArgumentResolverSupport {
|
||||
public abstract class HandlerMethodArgumentResolverSupport implements HandlerMethodArgumentResolver {
|
||||
|
||||
private final ReactiveAdapterRegistry adapterRegistry;
|
||||
|
||||
@@ -55,12 +56,12 @@ public abstract class HandlerMethodArgumentResolverSupport {
|
||||
* Evaluate the {@code Predicate} on the the method parameter type or on
|
||||
* the generic type within a reactive type wrapper.
|
||||
*/
|
||||
protected boolean checkParamType(MethodParameter param, Predicate<Class<?>> predicate) {
|
||||
Class<?> type = param.getParameterType();
|
||||
protected boolean checkParameterType(MethodParameter parameter, Predicate<Class<?>> predicate) {
|
||||
Class<?> type = parameter.getParameterType();
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type);
|
||||
if (adapter != null) {
|
||||
assertHasValues(adapter, param);
|
||||
type = param.nested().getNestedParameterType();
|
||||
assertHasValues(adapter, parameter);
|
||||
type = parameter.nested().getNestedParameterType();
|
||||
}
|
||||
return predicate.test(type);
|
||||
}
|
||||
@@ -77,25 +78,25 @@ public abstract class HandlerMethodArgumentResolverSupport {
|
||||
* {@code IllegalStateException} if the same matches the generic type
|
||||
* within a reactive type wrapper.
|
||||
*/
|
||||
protected boolean checkParamTypeNoReactiveWrapper(MethodParameter param, Predicate<Class<?>> predicate) {
|
||||
Class<?> type = param.getParameterType();
|
||||
protected boolean checkParameterTypeNoReactiveWrapper(MethodParameter parameter, Predicate<Class<?>> predicate) {
|
||||
Class<?> type = parameter.getParameterType();
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type);
|
||||
if (adapter != null) {
|
||||
assertHasValues(adapter, param);
|
||||
type = param.nested().getNestedParameterType();
|
||||
assertHasValues(adapter, parameter);
|
||||
type = parameter.nested().getNestedParameterType();
|
||||
}
|
||||
if (predicate.test(type)) {
|
||||
if (adapter == null) {
|
||||
return true;
|
||||
}
|
||||
throw getReactiveWrapperError(param);
|
||||
throw buildReactiveWrapperException(parameter);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private IllegalStateException getReactiveWrapperError(MethodParameter param) {
|
||||
private IllegalStateException buildReactiveWrapperException(MethodParameter parameter) {
|
||||
return new IllegalStateException(getClass().getSimpleName() +
|
||||
" doesn't support reactive type wrapper: " + param.getGenericParameterType());
|
||||
" doesn't support reactive type wrapper: " + parameter.getGenericParameterType());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,29 +106,28 @@ public abstract class HandlerMethodArgumentResolverSupport {
|
||||
* type within a reactive type wrapper.
|
||||
*/
|
||||
protected <A extends Annotation> boolean checkAnnotatedParamNoReactiveWrapper(
|
||||
MethodParameter param, Class<A> annotationType,
|
||||
BiPredicate<A, Class<?>> typePredicate) {
|
||||
MethodParameter parameter, Class<A> annotationType, BiPredicate<A, Class<?>> typePredicate) {
|
||||
|
||||
A annotation = param.getParameterAnnotation(annotationType);
|
||||
A annotation = parameter.getParameterAnnotation(annotationType);
|
||||
if (annotation == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
param = param.nestedIfOptional();
|
||||
Class<?> type = param.getNestedParameterType();
|
||||
parameter = parameter.nestedIfOptional();
|
||||
Class<?> type = parameter.getNestedParameterType();
|
||||
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type);
|
||||
if (adapter != null) {
|
||||
assertHasValues(adapter, param);
|
||||
param = param.nested();
|
||||
type = param.getNestedParameterType();
|
||||
assertHasValues(adapter, parameter);
|
||||
parameter = parameter.nested();
|
||||
type = parameter.getNestedParameterType();
|
||||
}
|
||||
|
||||
if (typePredicate.test(annotation, type)) {
|
||||
if (adapter == null) {
|
||||
return true;
|
||||
}
|
||||
throw getReactiveWrapperError(param);
|
||||
throw buildReactiveWrapperException(parameter);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -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.
|
||||
@@ -33,15 +33,14 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
*/
|
||||
public interface SyncHandlerMethodArgumentResolver extends HandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>By default this simply delegates to {@link #resolveArgumentValue} for
|
||||
* synchronous resolution.
|
||||
*/
|
||||
@Override
|
||||
default Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
default Mono<Object> resolveArgument(
|
||||
MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
return Mono.justOrEmpty(resolveArgumentValue(parameter, bindingContext, exchange));
|
||||
}
|
||||
@@ -53,7 +52,7 @@ public interface SyncHandlerMethodArgumentResolver extends HandlerMethodArgument
|
||||
* @param exchange the current exchange
|
||||
* @return an {@code Optional} with the resolved value, possibly empty
|
||||
*/
|
||||
Optional<Object> resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange);
|
||||
Optional<Object> resolveArgumentValue(
|
||||
MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange);
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -114,7 +114,6 @@ public abstract class AbstractMessageReaderArgumentResolver extends HandlerMetho
|
||||
}
|
||||
|
||||
for (ServerHttpMessageReader<?> reader : getMessageReaders()) {
|
||||
|
||||
if (reader.canRead(elementType, mediaType)) {
|
||||
Map<String, Object> readHints = Collections.emptyMap();
|
||||
if (adapter != null && adapter.isMultiValue()) {
|
||||
@@ -171,9 +170,9 @@ public abstract class AbstractMessageReaderArgumentResolver extends HandlerMetho
|
||||
private Object[] extractValidationHints(MethodParameter parameter) {
|
||||
Annotation[] annotations = parameter.getParameterAnnotations();
|
||||
for (Annotation ann : annotations) {
|
||||
Validated validAnnot = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||
if (validAnnot != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
||||
Object hints = (validAnnot != null ? validAnnot.value() : AnnotationUtils.getValue(ann));
|
||||
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
|
||||
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
|
||||
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
|
||||
return (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ValueConstants;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport;
|
||||
import org.springframework.web.server.ServerErrorException;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -58,8 +57,7 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements HandlerMethodArgumentResolver {
|
||||
public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
||||
|
||||
private final ConfigurableBeanFactory configurableBeanFactory;
|
||||
|
||||
@@ -69,23 +67,21 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
||||
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...} placeholder
|
||||
* @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 contain expressions
|
||||
* @param adapterRegistry for checking reactive type wrappers
|
||||
* @param registry for checking reactive type wrappers
|
||||
*/
|
||||
public AbstractNamedValueArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(adapterRegistry);
|
||||
this.configurableBeanFactory = beanFactory;
|
||||
this.expressionContext = (beanFactory != null ? new BeanExpressionContext(beanFactory, null) : null);
|
||||
public AbstractNamedValueArgumentResolver(ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry) {
|
||||
super(registry);
|
||||
this.configurableBeanFactory = factory;
|
||||
this.expressionContext = (factory != null ? new BeanExpressionContext(factory, null) : null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
public Mono<Object> resolveArgument(
|
||||
MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
|
||||
MethodParameter nestedParameter = parameter.nestedIfOptional();
|
||||
@@ -175,8 +171,7 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
||||
* @param exchange the current exchange
|
||||
* @return the resolved argument (may be {@code null})
|
||||
*/
|
||||
protected abstract Mono<Object> resolveName(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange);
|
||||
protected abstract Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange);
|
||||
|
||||
/**
|
||||
* Apply type conversion if necessary.
|
||||
@@ -277,8 +272,8 @@ public abstract class AbstractNamedValueArgumentResolver extends HandlerMethodAr
|
||||
* @param exchange the current exchange
|
||||
*/
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
protected void handleResolvedValue(Object arg, String name, MethodParameter parameter,
|
||||
Model model, ServerWebExchange exchange) {
|
||||
protected void handleResolvedValue(
|
||||
Object arg, String name, MethodParameter parameter, Model model, ServerWebExchange exchange) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -39,23 +39,20 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
public abstract class AbstractNamedValueSyncArgumentResolver extends AbstractNamedValueArgumentResolver
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...}
|
||||
* @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 adapterRegistry for checking reactive type wrappers
|
||||
* @param registry for checking reactive type wrappers
|
||||
*/
|
||||
protected AbstractNamedValueSyncArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(beanFactory, adapterRegistry);
|
||||
protected AbstractNamedValueSyncArgumentResolver(ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry) {
|
||||
super(factory, registry);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
public Mono<Object> resolveArgument(
|
||||
MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
// Flip the default implementation from SyncHandlerMethodArgumentResolver:
|
||||
// instead of delegating to (sync) resolveArgumentValue,
|
||||
@@ -66,8 +63,8 @@ public abstract class AbstractNamedValueSyncArgumentResolver extends AbstractNam
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> resolveArgumentValue(MethodParameter parameter,
|
||||
BindingContext context, ServerWebExchange exchange) {
|
||||
public Optional<Object> resolveArgumentValue(
|
||||
MethodParameter parameter, BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
// This won't block since resolveName below doesn't
|
||||
Object value = resolveArgument(parameter, context, exchange).block();
|
||||
@@ -76,16 +73,13 @@ public abstract class AbstractNamedValueSyncArgumentResolver extends AbstractNam
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Mono<Object> resolveName(String name, MethodParameter param,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
protected final Mono<Object> resolveName(String name, MethodParameter param, ServerWebExchange exchange) {
|
||||
return Mono.justOrEmpty(resolveNamedValue(name, param, exchange));
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually resolve the value synchronously.
|
||||
*/
|
||||
protected abstract Optional<Object> resolveNamedValue(String name,
|
||||
MethodParameter param, ServerWebExchange exchange);
|
||||
protected abstract Optional<Object> resolveNamedValue(String name, MethodParameter param, ServerWebExchange exchange);
|
||||
|
||||
}
|
||||
|
||||
@@ -38,17 +38,14 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
*/
|
||||
public class CookieValueMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...}
|
||||
* @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 contain expressions
|
||||
* @param adapterRegistry for checking reactive type wrappers
|
||||
* @param registry for checking reactive type wrappers
|
||||
*/
|
||||
public CookieValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(beanFactory, adapterRegistry);
|
||||
public CookieValueMethodArgumentResolver(ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry) {
|
||||
super(factory, registry);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,9 +61,7 @@ public class CookieValueMethodArgumentResolver extends AbstractNamedValueSyncArg
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
HttpCookie cookie = exchange.getRequest().getCookies().getFirst(name);
|
||||
Class<?> paramType = parameter.getNestedParameterType();
|
||||
if (HttpCookie.class.isAssignableFrom(paramType)) {
|
||||
|
||||
@@ -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.
|
||||
@@ -41,9 +41,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ErrorsMethodArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements HandlerMethodArgumentResolver {
|
||||
|
||||
public class ErrorsMethodArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
||||
|
||||
public ErrorsMethodArgumentResolver(ReactiveAdapterRegistry registry) {
|
||||
super(registry);
|
||||
@@ -52,13 +50,13 @@ public class ErrorsMethodArgumentResolver extends HandlerMethodArgumentResolverS
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return checkParamTypeNoReactiveWrapper(parameter, Errors.class::isAssignableFrom);
|
||||
return checkParameterTypeNoReactiveWrapper(parameter, Errors.class::isAssignableFrom);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
public Mono<Object> resolveArgument(
|
||||
MethodParameter parameter, BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
String name = getModelAttributeName(parameter);
|
||||
Object errors = context.getModel().asMap().get(BindingResult.MODEL_KEY_PREFIX + name);
|
||||
@@ -79,9 +77,8 @@ public class ErrorsMethodArgumentResolver extends HandlerMethodArgumentResolverS
|
||||
}
|
||||
|
||||
private String getModelAttributeName(MethodParameter parameter) {
|
||||
|
||||
Assert.isTrue(parameter.getParameterIndex() > 0,
|
||||
"Errors argument must be immediately after a model attribute argument.");
|
||||
"Errors argument must be immediately after a model attribute argument");
|
||||
|
||||
int index = parameter.getParameterIndex() - 1;
|
||||
MethodParameter attributeParam = new MethodParameter(parameter.getMethod(), index);
|
||||
|
||||
@@ -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.
|
||||
@@ -36,17 +36,14 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
*/
|
||||
public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...}
|
||||
* @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 contain expressions
|
||||
* @param adapterRegistry for checking reactive type wrappers
|
||||
* @param registry for checking reactive type wrappers
|
||||
*/
|
||||
public ExpressionValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(beanFactory, adapterRegistry);
|
||||
public ExpressionValueMethodArgumentResolver(ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry) {
|
||||
super(factory, registry);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,9 +59,7 @@ public class ExpressionValueMethodArgumentResolver extends AbstractNamedValueSyn
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
// No name to resolve
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -38,9 +38,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentResolver
|
||||
implements HandlerMethodArgumentResolver {
|
||||
|
||||
public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentResolver {
|
||||
|
||||
public HttpEntityArgumentResolver(List<ServerHttpMessageReader<?>> readers,
|
||||
ReactiveAdapterRegistry registry) {
|
||||
@@ -51,16 +49,15 @@ public class HttpEntityArgumentResolver extends AbstractMessageReaderArgumentRes
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return checkParamTypeNoReactiveWrapper(parameter,
|
||||
return checkParameterTypeNoReactiveWrapper(parameter,
|
||||
type -> HttpEntity.class.equals(type) || RequestEntity.class.equals(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
public Mono<Object> resolveArgument(
|
||||
MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
Class<?> entityType = parameter.getParameterType();
|
||||
|
||||
return readBody(parameter.nested(), false, bindingContext, exchange)
|
||||
.map(body -> createEntity(body, entityType, exchange.getRequest()))
|
||||
.defaultIfEmpty(createEntity(null, entityType, exchange.getRequest()));
|
||||
|
||||
@@ -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.
|
||||
@@ -36,7 +36,6 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
public class ModelArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
public ModelArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
|
||||
super(adapterRegistry);
|
||||
}
|
||||
@@ -44,7 +43,7 @@ public class ModelArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return checkParamTypeNoReactiveWrapper(parameter, Model.class::isAssignableFrom);
|
||||
return checkParameterTypeNoReactiveWrapper(parameter, Model.class::isAssignableFrom);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,30 +16,34 @@
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.MonoProcessor;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.support.WebExchangeBindException;
|
||||
import org.springframework.web.bind.support.WebExchangeDataBinder;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@@ -58,10 +62,12 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* attribute with or without the presence of an {@code @ModelAttribute}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements HandlerMethodArgumentResolver {
|
||||
public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
||||
|
||||
private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
private final boolean useDefaultResolution;
|
||||
|
||||
@@ -87,25 +93,25 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||
return true;
|
||||
}
|
||||
else if (this.useDefaultResolution) {
|
||||
return checkParamType(parameter, type -> !BeanUtils.isSimpleProperty(type));
|
||||
return checkParameterType(parameter, type -> !BeanUtils.isSimpleProperty(type));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
public Mono<Object> resolveArgument(
|
||||
MethodParameter parameter, BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
ResolvableType type = ResolvableType.forMethodParameter(parameter);
|
||||
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type.resolve());
|
||||
ResolvableType valueType = (adapter != null ? type.getGeneric(0) : type);
|
||||
|
||||
Assert.state(adapter == null || !adapter.isMultiValue(),
|
||||
getClass().getSimpleName() + " doesn't support multi-value reactive type wrapper: " +
|
||||
() -> getClass().getSimpleName() + " doesn't support multi-value reactive type wrapper: " +
|
||||
parameter.getGenericParameterType());
|
||||
|
||||
String name = getAttributeName(valueType, parameter);
|
||||
Mono<?> valueMono = getAttributeMono(name, valueType, context.getModel());
|
||||
Mono<?> valueMono = getAttributeMono(name, valueType, context, exchange);
|
||||
|
||||
Map<String, Object> model = context.getModel().asMap();
|
||||
MonoProcessor<BindingResult> bindingResultMono = MonoProcessor.create();
|
||||
@@ -140,22 +146,25 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||
}
|
||||
|
||||
private String getAttributeName(ResolvableType valueType, MethodParameter parameter) {
|
||||
ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);
|
||||
if (annot != null && StringUtils.hasText(annot.value())) {
|
||||
return annot.value();
|
||||
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
|
||||
if (ann != null && StringUtils.hasText(ann.value())) {
|
||||
return ann.value();
|
||||
}
|
||||
// TODO: Conventions does not deal with async wrappers
|
||||
return ClassUtils.getShortNameAsProperty(valueType.getRawClass());
|
||||
}
|
||||
|
||||
private Mono<?> getAttributeMono(String attributeName, ResolvableType attributeType, Model model) {
|
||||
Object attribute = model.asMap().get(attributeName);
|
||||
private Mono<?> getAttributeMono(
|
||||
String attributeName, ResolvableType attributeType, BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
Object attribute = context.getModel().asMap().get(attributeName);
|
||||
if (attribute == null) {
|
||||
attribute = BeanUtils.instantiateClass(attributeType.getRawClass());
|
||||
return createAttribute(attributeName, attributeType.getRawClass(), context, exchange);
|
||||
}
|
||||
|
||||
ReactiveAdapter adapterFrom = getAdapterRegistry().getAdapter(null, attribute);
|
||||
if (adapterFrom != null) {
|
||||
Assert.isTrue(!adapterFrom.isMultiValue(), "Data binding supports single-value async types.");
|
||||
Assert.isTrue(!adapterFrom.isMultiValue(), "Data binding only supports single-value async types");
|
||||
return Mono.from(adapterFrom.toPublisher(attribute));
|
||||
}
|
||||
else {
|
||||
@@ -163,10 +172,48 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasErrorsArgument(MethodParameter methodParam) {
|
||||
int i = methodParam.getParameterIndex();
|
||||
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
|
||||
return paramTypes.length > i && Errors.class.isAssignableFrom(paramTypes[i + 1]);
|
||||
private Mono<?> createAttribute(
|
||||
String attributeName, Class<?> attributeType, BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
Constructor<?>[] ctors = attributeType.getConstructors();
|
||||
if (ctors.length != 1) {
|
||||
// No standard data class or standard JavaBeans arrangement ->
|
||||
// defensively go with default constructor, expecting regular bean property bindings.
|
||||
return Mono.just(BeanUtils.instantiateClass(attributeType));
|
||||
}
|
||||
Constructor<?> ctor = ctors[0];
|
||||
if (ctor.getParameterCount() == 0) {
|
||||
// A single default constructor -> clearly a standard JavaBeans arrangement.
|
||||
return Mono.just(BeanUtils.instantiateClass(ctor));
|
||||
}
|
||||
|
||||
// A single data class constructor -> resolve constructor arguments from request parameters.
|
||||
return exchange.getRequestParams().then(requestParams -> {
|
||||
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);
|
||||
Class<?>[] paramTypes = ctor.getParameterTypes();
|
||||
Assert.state(paramNames.length == paramTypes.length,
|
||||
() -> "Invalid number of parameter names: " + paramNames.length + " for constructor " + ctor);
|
||||
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));
|
||||
}
|
||||
return Mono.fromSupplier(() -> BeanUtils.instantiateClass(ctor, args));
|
||||
});
|
||||
}
|
||||
|
||||
private boolean hasErrorsArgument(MethodParameter parameter) {
|
||||
int i = parameter.getParameterIndex();
|
||||
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
|
||||
return (paramTypes.length > i && Errors.class.isAssignableFrom(paramTypes[i + 1]));
|
||||
}
|
||||
|
||||
private void validateIfApplicable(WebExchangeDataBinder binder, MethodParameter parameter) {
|
||||
|
||||
@@ -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.
|
||||
@@ -43,7 +43,6 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
public class PathVariableMapMethodArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
public PathVariableMapMethodArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
|
||||
super(adapterRegistry);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -52,17 +52,14 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
*/
|
||||
public class PathVariableMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...}
|
||||
* @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 contain expressions
|
||||
* @param adapterRegistry for checking reactive type wrappers
|
||||
* @param registry for checking reactive type wrappers
|
||||
*/
|
||||
public PathVariableMethodArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(beanFactory, adapterRegistry);
|
||||
public PathVariableMethodArgumentResolver(ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry) {
|
||||
super(factory, registry);
|
||||
}
|
||||
|
||||
|
||||
@@ -83,9 +80,7 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueSyncAr
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
String attributeName = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
|
||||
return exchange.getAttribute(attributeName)
|
||||
.map(value -> ((Map<String, String>) value).get(name));
|
||||
@@ -98,8 +93,8 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueSyncAr
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void handleResolvedValue(Object arg, String name, MethodParameter parameter,
|
||||
Model model, ServerWebExchange exchange) {
|
||||
protected void handleResolvedValue(
|
||||
Object arg, String name, MethodParameter parameter, Model model, ServerWebExchange exchange) {
|
||||
|
||||
// TODO: View.PATH_VARIABLES ?
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -24,7 +24,6 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@@ -35,9 +34,7 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* @since 5.0
|
||||
* @see ServerWebExchangeArgumentResolver
|
||||
*/
|
||||
public class PrincipalArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements HandlerMethodArgumentResolver {
|
||||
|
||||
public class PrincipalArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
||||
|
||||
public PrincipalArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
|
||||
super(adapterRegistry);
|
||||
@@ -46,7 +43,7 @@ public class PrincipalArgumentResolver extends HandlerMethodArgumentResolverSupp
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return checkParamTypeNoReactiveWrapper(parameter, Principal.class::isAssignableFrom);
|
||||
return checkParameterTypeNoReactiveWrapper(parameter, Principal.class::isAssignableFrom);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,15 +36,13 @@ public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueSy
|
||||
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...}
|
||||
* @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 adapterRegistry for checking reactive type wrappers
|
||||
* @param registry for checking reactive type wrappers
|
||||
*/
|
||||
public RequestAttributeMethodArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(beanFactory, adapterRegistry);
|
||||
public RequestAttributeMethodArgumentResolver(ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry) {
|
||||
super(factory, registry);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,9 +59,7 @@ public class RequestAttributeMethodArgumentResolver extends AbstractNamedValueSy
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
protected Optional<Object> resolveNamedValue(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
return exchange.getAttribute(name);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -43,9 +43,7 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class RequestBodyArgumentResolver extends AbstractMessageReaderArgumentResolver
|
||||
implements HandlerMethodArgumentResolver {
|
||||
|
||||
public class RequestBodyArgumentResolver extends AbstractMessageReaderArgumentResolver {
|
||||
|
||||
public RequestBodyArgumentResolver(List<ServerHttpMessageReader<?>> readers,
|
||||
ReactiveAdapterRegistry registry) {
|
||||
@@ -60,8 +58,8 @@ public class RequestBodyArgumentResolver extends AbstractMessageReaderArgumentRe
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter param, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
public Mono<Object> resolveArgument(
|
||||
MethodParameter param, BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
|
||||
RequestBody annotation = param.getParameterAnnotation(RequestBody.class);
|
||||
return readBody(param, annotation.required(), bindingContext, exchange);
|
||||
|
||||
@@ -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.
|
||||
@@ -45,7 +45,6 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
public class RequestHeaderMapMethodArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
public RequestHeaderMapMethodArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
|
||||
super(adapterRegistry);
|
||||
}
|
||||
|
||||
@@ -47,15 +47,13 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueSyncArgumentResolver {
|
||||
|
||||
/**
|
||||
* @param beanFactory a bean factory to use for resolving ${...}
|
||||
* @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 adapterRegistry for checking reactive type wrappers
|
||||
* @param registry for checking reactive type wrappers
|
||||
*/
|
||||
public RequestHeaderMethodArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(beanFactory, adapterRegistry);
|
||||
public RequestHeaderMethodArgumentResolver(ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry) {
|
||||
super(factory, registry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
public class RequestParamMapMethodArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
public RequestParamMapMethodArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
|
||||
super(adapterRegistry);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -59,19 +59,19 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueSyncAr
|
||||
|
||||
/**
|
||||
* Class constructor with a default resolution mode flag.
|
||||
* @param beanFactory a bean factory used for resolving ${...} placeholder
|
||||
* @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 adapterRegistry for checking reactive type wrappers
|
||||
* @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 RequestParamMethodArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
ReactiveAdapterRegistry adapterRegistry, boolean useDefaultResolution) {
|
||||
public RequestParamMethodArgumentResolver(
|
||||
ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry, boolean useDefaultResolution) {
|
||||
|
||||
super(beanFactory, adapterRegistry);
|
||||
super(factory, registry);
|
||||
this.useDefaultResolution = useDefaultResolution;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueSyncAr
|
||||
return true;
|
||||
}
|
||||
else if (this.useDefaultResolution) {
|
||||
return checkParamTypeNoReactiveWrapper(param, BeanUtils::isSimpleProperty);
|
||||
return checkParameterTypeNoReactiveWrapper(param, BeanUtils::isSimpleProperty);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -48,7 +48,6 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
public class ServerWebExchangeArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
|
||||
public ServerWebExchangeArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
|
||||
super(adapterRegistry);
|
||||
}
|
||||
@@ -56,7 +55,7 @@ public class ServerWebExchangeArgumentResolver extends HandlerMethodArgumentReso
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return checkParamTypeNoReactiveWrapper(parameter,
|
||||
return checkParameterTypeNoReactiveWrapper(parameter,
|
||||
type -> ServerWebExchange.class.isAssignableFrom(type) ||
|
||||
ServerHttpRequest.class.isAssignableFrom(type) ||
|
||||
ServerHttpResponse.class.isAssignableFrom(type) ||
|
||||
|
||||
@@ -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.
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -36,11 +37,8 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
*/
|
||||
public class SessionAttributeMethodArgumentResolver extends AbstractNamedValueArgumentResolver {
|
||||
|
||||
|
||||
public SessionAttributeMethodArgumentResolver(ConfigurableBeanFactory beanFactory,
|
||||
ReactiveAdapterRegistry adapterRegistry) {
|
||||
|
||||
super(beanFactory, adapterRegistry);
|
||||
public SessionAttributeMethodArgumentResolver(ConfigurableBeanFactory factory, ReactiveAdapterRegistry registry) {
|
||||
super(factory, registry);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,9 +54,7 @@ public class SessionAttributeMethodArgumentResolver extends AbstractNamedValueAr
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
protected Mono<Object> resolveName(String name, MethodParameter parameter, ServerWebExchange exchange) {
|
||||
return exchange.getSession()
|
||||
.map(session -> session.getAttribute(name))
|
||||
.filter(Optional::isPresent)
|
||||
|
||||
@@ -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.
|
||||
@@ -34,9 +34,7 @@ import org.springframework.web.server.WebSession;
|
||||
* @since 5.0
|
||||
* @see ServerWebExchangeArgumentResolver
|
||||
*/
|
||||
public class WebSessionArgumentResolver extends HandlerMethodArgumentResolverSupport
|
||||
implements HandlerMethodArgumentResolver {
|
||||
|
||||
public class WebSessionArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
||||
|
||||
public WebSessionArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
|
||||
super(adapterRegistry);
|
||||
@@ -45,12 +43,12 @@ public class WebSessionArgumentResolver extends HandlerMethodArgumentResolverSup
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return checkParamTypeNoReactiveWrapper(parameter, WebSession.class::isAssignableFrom);
|
||||
return checkParameterTypeNoReactiveWrapper(parameter, WebSession.class::isAssignableFrom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext context,
|
||||
ServerWebExchange exchange) {
|
||||
public Mono<Object> resolveArgument(
|
||||
MethodParameter parameter, BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
Assert.isAssignable(WebSession.class, parameter.getParameterType());
|
||||
return exchange.getSession().cast(Object.class);
|
||||
|
||||
@@ -56,12 +56,9 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebInputException;
|
||||
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.core.ResolvableType.forClassWithGenerics;
|
||||
import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.post;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.ResolvableType.*;
|
||||
import static org.springframework.mock.http.server.reactive.test.MockServerHttpRequest.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AbstractMessageReaderArgumentResolver}.
|
||||
@@ -307,7 +304,16 @@ public class MessageReaderArgumentResolverTests {
|
||||
private AbstractMessageReaderArgumentResolver resolver(Decoder<?>... decoders) {
|
||||
List<ServerHttpMessageReader<?>> readers = new ArrayList<>();
|
||||
Arrays.asList(decoders).forEach(decoder -> readers.add(new DecoderHttpMessageReader<>(decoder)));
|
||||
return new AbstractMessageReaderArgumentResolver(readers) {};
|
||||
return new AbstractMessageReaderArgumentResolver(readers) {
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -42,16 +42,13 @@ import org.springframework.web.method.ResolvableMethod;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ModelAttributeMethodArgumentResolver}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class ModelAttributeMethodArgumentResolverTests {
|
||||
|
||||
@@ -116,7 +113,6 @@ public class ModelAttributeMethodArgumentResolverTests {
|
||||
|
||||
@Test
|
||||
public void createAndBindToMono() throws Exception {
|
||||
|
||||
MethodParameter parameter = this.testMethod
|
||||
.annotNotPresent(ModelAttribute.class).arg(Mono.class, Foo.class);
|
||||
|
||||
@@ -130,7 +126,6 @@ public class ModelAttributeMethodArgumentResolverTests {
|
||||
|
||||
@Test
|
||||
public void createAndBindToSingle() throws Exception {
|
||||
|
||||
MethodParameter parameter = this.testMethod
|
||||
.annotPresent(ModelAttribute.class).arg(Single.class, Foo.class);
|
||||
|
||||
@@ -211,6 +206,7 @@ public class ModelAttributeMethodArgumentResolverTests {
|
||||
|
||||
Foo foo = valueExtractor.apply(value);
|
||||
assertEquals("Robert", foo.getName());
|
||||
assertEquals(25, foo.getAge());
|
||||
|
||||
String key = "foo";
|
||||
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + key;
|
||||
@@ -231,7 +227,6 @@ public class ModelAttributeMethodArgumentResolverTests {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void validationErrorToMono() throws Exception {
|
||||
|
||||
MethodParameter parameter = this.testMethod
|
||||
.annotNotPresent(ModelAttribute.class).arg(Mono.class, Foo.class);
|
||||
|
||||
@@ -246,7 +241,6 @@ public class ModelAttributeMethodArgumentResolverTests {
|
||||
|
||||
@Test
|
||||
public void validationErrorToSingle() throws Exception {
|
||||
|
||||
MethodParameter parameter = this.testMethod
|
||||
.annotPresent(ModelAttribute.class).arg(Single.class, Foo.class);
|
||||
|
||||
@@ -264,7 +258,6 @@ public class ModelAttributeMethodArgumentResolverTests {
|
||||
|
||||
ServerWebExchange exchange = postForm("age=invalid");
|
||||
Mono<?> mono = createResolver().resolveArgument(param, this.bindContext, exchange);
|
||||
|
||||
mono = valueMonoExtractor.apply(mono);
|
||||
|
||||
StepVerifier.create(mono)
|
||||
@@ -277,6 +270,31 @@ public class ModelAttributeMethodArgumentResolverTests {
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindDataClass() throws Exception {
|
||||
testBindBar(this.testMethod.annotNotPresent(ModelAttribute.class).arg(Bar.class));
|
||||
}
|
||||
|
||||
private void testBindBar(MethodParameter param) throws Exception {
|
||||
Object value = createResolver()
|
||||
.resolveArgument(param, this.bindContext, postForm("name=Robert&age=25&count=1"))
|
||||
.block(Duration.ZERO);
|
||||
|
||||
Bar bar = (Bar) value;
|
||||
assertEquals("Robert", bar.getName());
|
||||
assertEquals(25, bar.getAge());
|
||||
assertEquals(1, bar.getCount());
|
||||
|
||||
String key = "bar";
|
||||
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + key;
|
||||
|
||||
Map<String, Object> map = bindContext.getModel().asMap();
|
||||
assertEquals(map.toString(), 2, map.size());
|
||||
assertSame(bar, map.get(key));
|
||||
assertNotNull(map.get(bindingResultKey));
|
||||
assertTrue(map.get(bindingResultKey) instanceof BindingResult);
|
||||
}
|
||||
|
||||
|
||||
private ModelAttributeMethodArgumentResolver createResolver() {
|
||||
return new ModelAttributeMethodArgumentResolver(new ReactiveAdapterRegistry(), false);
|
||||
@@ -298,7 +316,8 @@ public class ModelAttributeMethodArgumentResolverTests {
|
||||
Foo fooNotAnnotated,
|
||||
String stringNotAnnotated,
|
||||
Mono<Foo> monoNotAnnotated,
|
||||
Mono<String> monoStringNotAnnotated) {
|
||||
Mono<String> monoStringNotAnnotated,
|
||||
Bar barNotAnnotated) {
|
||||
}
|
||||
|
||||
|
||||
@@ -333,4 +352,35 @@ public class ModelAttributeMethodArgumentResolverTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class Bar {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final int age;
|
||||
|
||||
private int count;
|
||||
|
||||
public Bar(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return this.age;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -89,7 +89,7 @@ public class ModelAndView {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new ModelAndView given a view name and a model.
|
||||
* Create a new ModelAndView given a view name and a model.
|
||||
* @param viewName name of the View to render, to be resolved
|
||||
* by the DispatcherServlet's ViewResolver
|
||||
* @param model Map of model names (Strings) to model objects
|
||||
@@ -104,7 +104,7 @@ public class ModelAndView {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new ModelAndView given a View object and a model.
|
||||
* Create a new ModelAndView given a View object and a model.
|
||||
* <emphasis>Note: the supplied model data is copied into the internal
|
||||
* storage of this class. You should not consider to modify the supplied
|
||||
* Map after supplying it to this class</emphasis>
|
||||
@@ -121,14 +121,27 @@ public class ModelAndView {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new ModelAndView given a view name, model, and status.
|
||||
* Create a new ModelAndView given a view name and HTTP status.
|
||||
* @param viewName name of the View to render, to be resolved
|
||||
* by the DispatcherServlet's ViewResolver
|
||||
* @param status an HTTP status code to use for the response
|
||||
* (to be set just prior to View rendering)
|
||||
* @since 4.3.8
|
||||
*/
|
||||
public ModelAndView(String viewName, HttpStatus status) {
|
||||
this.view = viewName;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ModelAndView given a view name, model, and HTTP status.
|
||||
* @param viewName name of the View to render, to be resolved
|
||||
* by the DispatcherServlet's ViewResolver
|
||||
* @param model Map of model names (Strings) to model objects
|
||||
* (Objects). Model entries may not be {@code null}, but the
|
||||
* model Map may be {@code null} if there is no model data.
|
||||
* @param status an alternative status code to use for the response;
|
||||
* The response status is set just prior to View rendering.
|
||||
* @param status an HTTP status code to use for the response
|
||||
* (to be set just prior to View rendering)
|
||||
* @since 4.3
|
||||
*/
|
||||
public ModelAndView(String viewName, Map<String, ?> model, HttpStatus status) {
|
||||
|
||||
@@ -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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
@@ -140,14 +141,7 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
import org.springframework.web.servlet.support.RequestContextUtils;
|
||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -283,15 +277,13 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
}, NestedSetController.class);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||
request.addParameter("testBeanSet", new String[] {"1", "2"});
|
||||
request.addParameter("testBeanSet", "1", "2");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
getServlet().service(request, response);
|
||||
assertEquals("[1, 2]-org.springframework.tests.sample.beans.TestBean", response.getContentAsString());
|
||||
}
|
||||
|
||||
// SPR-12903
|
||||
|
||||
@Test
|
||||
@Test // SPR-12903
|
||||
public void pathVariableWithCustomConverter() throws Exception {
|
||||
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
|
||||
@Override
|
||||
@@ -1244,9 +1236,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
assertEquals("myParam-42", response.getContentAsString());
|
||||
}
|
||||
|
||||
// SPR-9062
|
||||
|
||||
@Test
|
||||
@Test // SPR-9062
|
||||
public void ambiguousPathAndRequestMethod() throws Exception {
|
||||
initServletWithControllers(AmbiguousPathAndRequestMethodController.class);
|
||||
|
||||
@@ -1498,9 +1488,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
assertEquals(405, response.getStatus());
|
||||
}
|
||||
|
||||
// SPR-8536
|
||||
|
||||
@Test
|
||||
@Test // SPR-8536
|
||||
public void testHeadersCondition() throws Exception {
|
||||
initServletWithControllers(HeadersConditionController.class);
|
||||
|
||||
@@ -1571,7 +1559,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
assertTrue(RequestContextUtils.getOutputFlashMap(request).isEmpty());
|
||||
}
|
||||
|
||||
@Test // SPR-15176
|
||||
@Test // SPR-15176
|
||||
public void flashAttributesWithResponseEntity() throws Exception {
|
||||
initServletWithControllers(RedirectAttributesController.class);
|
||||
|
||||
@@ -1831,6 +1819,31 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
assertTrue(response.getContentAsByteArray().length == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataClassBinding() throws ServletException, IOException {
|
||||
initServletWithControllers(DataClassController.class);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bind");
|
||||
request.addParameter("param1", "value1");
|
||||
request.addParameter("param2", "2");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
getServlet().service(request, response);
|
||||
assertEquals("value1-2-0", response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataClassBindingWithAdditionalSetter() throws ServletException, IOException {
|
||||
initServletWithControllers(DataClassController.class);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bind");
|
||||
request.addParameter("param1", "value1");
|
||||
request.addParameter("param2", "2");
|
||||
request.addParameter("param3", "3");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
getServlet().service(request, response);
|
||||
assertEquals("value1-2-3", response.getContentAsString());
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
static class ControllerWithEmptyValueMapping {
|
||||
@@ -2727,15 +2740,12 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
|
||||
@XmlRootElement
|
||||
public static class A {
|
||||
|
||||
}
|
||||
|
||||
@XmlRootElement
|
||||
public static class B {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class NotReadableMessageConverter implements HttpMessageConverter<Object> {
|
||||
|
||||
@Override
|
||||
@@ -3313,7 +3323,6 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
public HttpHeaders createNoHeader() throws URISyntaxException {
|
||||
return new HttpHeaders();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
@@ -3345,7 +3354,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
|
||||
@RequestMapping("/path")
|
||||
public ModelAndView methodWithHttpStatus(MyEntity object) {
|
||||
return new ModelAndView("view", new ModelMap(), HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
return new ModelAndView("view", HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
@RequestMapping("/exception")
|
||||
@@ -3355,7 +3364,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
|
||||
@ExceptionHandler(TestException.class)
|
||||
public ModelAndView handleException() {
|
||||
return new ModelAndView("view", new ModelMap(), HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
return new ModelAndView("view", HttpStatus.UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@@ -3363,4 +3372,32 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataClass {
|
||||
|
||||
public final String param1;
|
||||
|
||||
public final int param2;
|
||||
|
||||
public int param3;
|
||||
|
||||
@ConstructorProperties({"param1", "param2"})
|
||||
public DataClass(String param1, int p2) {
|
||||
this.param1 = param1;
|
||||
this.param2 = p2;
|
||||
}
|
||||
|
||||
public void setParam3(int param3) {
|
||||
this.param3 = param3;
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
public static class DataClassController {
|
||||
|
||||
@RequestMapping("/bind")
|
||||
public String handle(DataClass data) {
|
||||
return data.param1 + "-" + data.param2 + "-" + data.param3;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user