Consistent java.util.Optional resolution, lenient handling of optional multipart files, correct Servlet 3.0 Part list/array selection
Issue: SPR-13418 Issue: SPR-13849 Issue: SPR-13850 Issue: SPR-13893
This commit is contained in:
parent
a3a5a03ee3
commit
b4f33adf48
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -27,17 +27,16 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class that encapsulates the specification of a method parameter, i.e.
|
* Helper class that encapsulates the specification of a method parameter, i.e. a {@link Method}
|
||||||
* a {@link Method} or {@link Constructor} plus a parameter index and a nested
|
* or {@link Constructor} plus a parameter index and a nested type index for a declared generic
|
||||||
* type index for a declared generic type. Useful as a specification object to
|
* type. Useful as a specification object to pass along.
|
||||||
* pass along.
|
|
||||||
*
|
*
|
||||||
* <p>As of 4.2, there is a {@link org.springframework.core.annotation.SynthesizingMethodParameter
|
* <p>As of 4.2, there is a {@link org.springframework.core.annotation.SynthesizingMethodParameter}
|
||||||
* SynthesizingMethodParameter} subclass available which synthesizes annotations
|
* subclass available which synthesizes annotations with attribute aliases. That subclass is used
|
||||||
* with attribute aliases. That subclass is used for web and message endpoint
|
* for web and message endpoint processing, in particular.
|
||||||
* processing, in particular.
|
|
||||||
*
|
*
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Rob Harrop
|
* @author Rob Harrop
|
||||||
|
|
@ -49,6 +48,18 @@ import org.springframework.util.Assert;
|
||||||
*/
|
*/
|
||||||
public class MethodParameter {
|
public class MethodParameter {
|
||||||
|
|
||||||
|
private static Class<?> javaUtilOptionalClass = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
javaUtilOptionalClass = ClassUtils.forName("java.util.Optional", MethodParameter.class.getClassLoader());
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ex) {
|
||||||
|
// Java 8 not available - Optional references simply not supported then.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private final Method method;
|
private final Method method;
|
||||||
|
|
||||||
private final Constructor<?> constructor;
|
private final Constructor<?> constructor;
|
||||||
|
|
@ -72,6 +83,8 @@ public class MethodParameter {
|
||||||
|
|
||||||
private volatile String parameterName;
|
private volatile String parameterName;
|
||||||
|
|
||||||
|
private volatile MethodParameter nestedMethodParameter;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@code MethodParameter} for the given method, with nesting level 1.
|
* Create a new {@code MethodParameter} for the given method, with nesting level 1.
|
||||||
|
|
@ -279,6 +292,44 @@ public class MethodParameter {
|
||||||
return this.typeIndexesPerLevel;
|
return this.typeIndexesPerLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a variant of this {@code MethodParameter} which points to the
|
||||||
|
* same parameter but one nesting level deeper. This is effectively the
|
||||||
|
* same as {@link #increaseNestingLevel()}, just with an independent
|
||||||
|
* {@code MethodParameter} object (e.g. in case of the original being cached).
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public MethodParameter nested() {
|
||||||
|
if (this.nestedMethodParameter != null) {
|
||||||
|
return this.nestedMethodParameter;
|
||||||
|
}
|
||||||
|
MethodParameter nestedParam = clone();
|
||||||
|
nestedParam.nestingLevel = this.nestingLevel + 1;
|
||||||
|
this.nestedMethodParameter = nestedParam;
|
||||||
|
return nestedParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this method parameter is declared as optiona
|
||||||
|
* in the form of Java 8's {@link java.util.Optional}.
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public boolean isOptional() {
|
||||||
|
return (getParameterType() == javaUtilOptionalClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a variant of this {@code MethodParameter} which points to
|
||||||
|
* the same parameter but one nesting level deeper in case of a
|
||||||
|
* {@link java.util.Optional} declaration.
|
||||||
|
* @since 4.3
|
||||||
|
* @see #isOptional()
|
||||||
|
* @see #nested()
|
||||||
|
*/
|
||||||
|
public MethodParameter nestedIfOptional() {
|
||||||
|
return (isOptional() ? nested() : this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a containing class to resolve the parameter type against.
|
* Set a containing class to resolve the parameter type against.
|
||||||
|
|
@ -370,8 +421,8 @@ public class MethodParameter {
|
||||||
/**
|
/**
|
||||||
* Return the nested generic type of the method/constructor parameter.
|
* Return the nested generic type of the method/constructor parameter.
|
||||||
* @return the parameter type (never {@code null})
|
* @return the parameter type (never {@code null})
|
||||||
* @see #getNestingLevel()
|
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
|
* @see #getNestingLevel()
|
||||||
*/
|
*/
|
||||||
public Type getNestedGenericParameterType() {
|
public Type getNestedGenericParameterType() {
|
||||||
if (this.nestingLevel > 1) {
|
if (this.nestingLevel > 1) {
|
||||||
|
|
@ -526,6 +577,11 @@ public class MethodParameter {
|
||||||
return (getMember().hashCode() * 31 + this.parameterIndex);
|
return (getMember().hashCode() * 31 + this.parameterIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodParameter clone() {
|
||||||
|
return new MethodParameter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new MethodParameter for the given method or constructor.
|
* Create a new MethodParameter for the given method or constructor.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -44,6 +44,10 @@ public class SynthesizingMethodParameter extends MethodParameter {
|
||||||
super(method, parameterIndex);
|
super(method, parameterIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected SynthesizingMethodParameter(SynthesizingMethodParameter original) {
|
||||||
|
super(original);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <A extends Annotation> A adaptAnnotation(A annotation) {
|
protected <A extends Annotation> A adaptAnnotation(A annotation) {
|
||||||
|
|
@ -55,4 +59,10 @@ public class SynthesizingMethodParameter extends MethodParameter {
|
||||||
return AnnotationUtils.synthesizeAnnotationArray(annotations, getAnnotatedElement());
|
return AnnotationUtils.synthesizeAnnotationArray(annotations, getAnnotatedElement());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SynthesizingMethodParameter clone() {
|
||||||
|
return new SynthesizingMethodParameter(this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -265,6 +265,10 @@ public class HandlerMethod {
|
||||||
super(HandlerMethod.this.bridgedMethod, index);
|
super(HandlerMethod.this.bridgedMethod, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected HandlerMethodParameter(HandlerMethodParameter original) {
|
||||||
|
super(original);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getContainingClass() {
|
public Class<?> getContainingClass() {
|
||||||
return HandlerMethod.this.getBeanType();
|
return HandlerMethod.this.getBeanType();
|
||||||
|
|
@ -274,6 +278,11 @@ public class HandlerMethod {
|
||||||
public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
|
public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
|
||||||
return HandlerMethod.this.getMethodAnnotation(annotationType);
|
return HandlerMethod.this.getMethodAnnotation(annotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandlerMethodParameter clone() {
|
||||||
|
return new HandlerMethodParameter(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -289,10 +298,20 @@ public class HandlerMethod {
|
||||||
this.returnValue = returnValue;
|
this.returnValue = returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
|
||||||
|
super(original);
|
||||||
|
this.returnValue = original.returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getParameterType() {
|
public Class<?> getParameterType() {
|
||||||
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
|
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReturnValueMethodParameter clone() {
|
||||||
|
return new ReturnValueMethodParameter(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -84,24 +84,24 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
|
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
|
||||||
Class<?> paramType = parameter.getParameterType();
|
|
||||||
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
|
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
|
||||||
|
MethodParameter nestedParameter = parameter.nestedIfOptional();
|
||||||
|
|
||||||
Object arg = resolveArgumentInternal(parameter, message, namedValueInfo.name);
|
Object arg = resolveArgumentInternal(nestedParameter, message, namedValueInfo.name);
|
||||||
if (arg == null) {
|
if (arg == null) {
|
||||||
if (namedValueInfo.defaultValue != null) {
|
if (namedValueInfo.defaultValue != null) {
|
||||||
arg = resolveDefaultValue(namedValueInfo.defaultValue);
|
arg = resolveDefaultValue(namedValueInfo.defaultValue);
|
||||||
}
|
}
|
||||||
else if (namedValueInfo.required && !parameter.getParameterType().getName().equals("java.util.Optional")) {
|
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
|
||||||
handleMissingValue(namedValueInfo.name, parameter, message);
|
handleMissingValue(namedValueInfo.name, nestedParameter, message);
|
||||||
}
|
}
|
||||||
arg = handleNullValue(namedValueInfo.name, arg, paramType);
|
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
|
||||||
}
|
}
|
||||||
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
|
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
|
||||||
arg = resolveDefaultValue(namedValueInfo.defaultValue);
|
arg = resolveDefaultValue(namedValueInfo.defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ClassUtils.isAssignableValue(paramType, arg)) {
|
if (!ClassUtils.isAssignableValue(parameter.getParameterType(), arg)) {
|
||||||
arg = this.conversionService.convert(
|
arg = this.conversionService.convert(
|
||||||
arg, TypeDescriptor.valueOf(arg.getClass()), new TypeDescriptor(parameter));
|
arg, TypeDescriptor.valueOf(arg.getClass()), new TypeDescriptor(parameter));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -42,6 +42,7 @@ public class DestinationVariableMethodArgumentResolver extends AbstractNamedValu
|
||||||
super(cs, null);
|
super(cs, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
return parameter.hasParameterAnnotation(DestinationVariable.class);
|
return parameter.hasParameterAnnotation(DestinationVariable.class);
|
||||||
|
|
@ -58,10 +59,9 @@ public class DestinationVariableMethodArgumentResolver extends AbstractNamedValu
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Map<String, String> vars = (Map<String, String>) message.getHeaders().get(
|
Map<String, String> vars =
|
||||||
DESTINATION_TEMPLATE_VARIABLES_HEADER);
|
(Map<String, String>) message.getHeaders().get(DESTINATION_TEMPLATE_VARIABLES_HEADER);
|
||||||
|
return (vars != null ? vars.get(name) : null);
|
||||||
return (vars != null) ? vars.get(name) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -77,4 +77,5 @@ public class DestinationVariableMethodArgumentResolver extends AbstractNamedValu
|
||||||
super(annotation.value(), true, ValueConstants.DEFAULT_NONE);
|
super(annotation.value(), true, ValueConstants.DEFAULT_NONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -272,6 +272,12 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||||
this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(0);
|
this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected AsyncResultMethodParameter(AsyncResultMethodParameter original) {
|
||||||
|
super(original);
|
||||||
|
this.returnValue = original.returnValue;
|
||||||
|
this.returnType = original.returnType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getParameterType() {
|
public Class<?> getParameterType() {
|
||||||
if (this.returnValue != null) {
|
if (this.returnValue != null) {
|
||||||
|
|
@ -287,6 +293,11 @@ public class InvocableHandlerMethod extends HandlerMethod {
|
||||||
public Type getGenericParameterType() {
|
public Type getGenericParameterType() {
|
||||||
return this.returnType.getType();
|
return this.returnType.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsyncResultMethodParameter clone() {
|
||||||
|
return new AsyncResultMethodParameter(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -55,7 +55,6 @@ import org.springframework.web.multipart.MultipartResolver;
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*
|
|
||||||
* @see RequestParam
|
* @see RequestParam
|
||||||
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
|
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -280,6 +280,10 @@ public class HandlerMethod {
|
||||||
super(HandlerMethod.this.bridgedMethod, index);
|
super(HandlerMethod.this.bridgedMethod, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected HandlerMethodParameter(HandlerMethodParameter original) {
|
||||||
|
super(original);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getContainingClass() {
|
public Class<?> getContainingClass() {
|
||||||
return HandlerMethod.this.getBeanType();
|
return HandlerMethod.this.getBeanType();
|
||||||
|
|
@ -289,6 +293,11 @@ public class HandlerMethod {
|
||||||
public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
|
public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
|
||||||
return HandlerMethod.this.getMethodAnnotation(annotationType);
|
return HandlerMethod.this.getMethodAnnotation(annotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandlerMethodParameter clone() {
|
||||||
|
return new HandlerMethodParameter(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -304,10 +313,20 @@ public class HandlerMethod {
|
||||||
this.returnValue = returnValue;
|
this.returnValue = returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
|
||||||
|
super(original);
|
||||||
|
this.returnValue = original.returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getParameterType() {
|
public Class<?> getParameterType() {
|
||||||
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
|
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReturnValueMethodParameter clone() {
|
||||||
|
return new ReturnValueMethodParameter(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -63,7 +63,7 @@ public abstract class AbstractCookieValueMethodArgumentResolver extends Abstract
|
||||||
@Override
|
@Override
|
||||||
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
|
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
|
||||||
throw new ServletRequestBindingException("Missing cookie '" + name +
|
throw new ServletRequestBindingException("Missing cookie '" + name +
|
||||||
"' for method parameter of type " + parameter.getParameterType().getSimpleName());
|
"' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -26,6 +26,7 @@ import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.web.bind.ServletRequestBindingException;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.ValueConstants;
|
import org.springframework.web.bind.annotation.ValueConstants;
|
||||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
|
|
@ -53,6 +54,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||||
|
|
@ -61,7 +63,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||||
|
|
||||||
private final BeanExpressionContext expressionContext;
|
private final BeanExpressionContext expressionContext;
|
||||||
|
|
||||||
private Map<MethodParameter, NamedValueInfo> namedValueInfoCache = new ConcurrentHashMap<MethodParameter, NamedValueInfo>(256);
|
private final Map<MethodParameter, NamedValueInfo> namedValueInfoCache = new ConcurrentHashMap<MethodParameter, NamedValueInfo>(256);
|
||||||
|
|
||||||
|
|
||||||
public AbstractNamedValueMethodArgumentResolver() {
|
public AbstractNamedValueMethodArgumentResolver() {
|
||||||
|
|
@ -84,18 +86,18 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||||
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
|
||||||
|
|
||||||
Class<?> paramType = parameter.getParameterType();
|
|
||||||
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
|
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
|
||||||
|
MethodParameter nestedParameter = parameter.nestedIfOptional();
|
||||||
|
|
||||||
Object arg = resolveName(namedValueInfo.name, parameter, webRequest);
|
Object arg = resolveName(namedValueInfo.name, nestedParameter, webRequest);
|
||||||
if (arg == null) {
|
if (arg == null) {
|
||||||
if (namedValueInfo.defaultValue != null) {
|
if (namedValueInfo.defaultValue != null) {
|
||||||
arg = resolveDefaultValue(namedValueInfo.defaultValue);
|
arg = resolveDefaultValue(namedValueInfo.defaultValue);
|
||||||
}
|
}
|
||||||
else if (namedValueInfo.required && !parameter.getParameterType().getName().equals("java.util.Optional")) {
|
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
|
||||||
handleMissingValue(namedValueInfo.name, parameter);
|
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
|
||||||
}
|
}
|
||||||
arg = handleNullValue(namedValueInfo.name, arg, paramType);
|
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
|
||||||
}
|
}
|
||||||
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
|
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
|
||||||
arg = resolveDefaultValue(namedValueInfo.defaultValue);
|
arg = resolveDefaultValue(namedValueInfo.defaultValue);
|
||||||
|
|
@ -104,7 +106,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||||
if (binderFactory != null) {
|
if (binderFactory != null) {
|
||||||
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
|
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
|
||||||
try {
|
try {
|
||||||
arg = binder.convertIfNecessary(arg, paramType, parameter);
|
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
|
||||||
}
|
}
|
||||||
catch (ConversionNotSupportedException ex) {
|
catch (ConversionNotSupportedException ex) {
|
||||||
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
|
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
|
||||||
|
|
@ -151,7 +153,8 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||||
if (info.name.length() == 0) {
|
if (info.name.length() == 0) {
|
||||||
name = parameter.getParameterName();
|
name = parameter.getParameterName();
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
throw new IllegalArgumentException("Name for argument type [" + parameter.getParameterType().getName() +
|
throw new IllegalArgumentException(
|
||||||
|
"Name for argument type [" + parameter.getNestedParameterType().getName() +
|
||||||
"] not available, and parameter name information not found in class file either.");
|
"] not available, and parameter name information not found in class file either.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -160,18 +163,19 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the given parameter type and value name into an argument value.
|
* Resolve the given parameter type and value name into an argument value.
|
||||||
* @param name the name of the value being resolved
|
* @param name the name of the value being resolved
|
||||||
* @param parameter the method parameter to resolve to an argument value
|
* @param parameter the method parameter to resolve to an argument value
|
||||||
|
* (pre-nested in case of a {@link java.util.Optional} declaration)
|
||||||
* @param request the current request
|
* @param request the current request
|
||||||
* @return the resolved argument. May be {@code null}
|
* @return the resolved argument (may be {@code null})
|
||||||
* @throws Exception in case of errors
|
* @throws Exception in case of errors
|
||||||
*/
|
*/
|
||||||
protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request)
|
protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request)
|
||||||
throws Exception;
|
throws Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the given default value into an argument value.
|
* Resolve the given default value into an argument value.
|
||||||
*/
|
*/
|
||||||
private Object resolveDefaultValue(String defaultValue) {
|
private Object resolveDefaultValue(String defaultValue) {
|
||||||
if (this.configurableBeanFactory == null) {
|
if (this.configurableBeanFactory == null) {
|
||||||
|
|
@ -190,8 +194,23 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||||
* returned {@code null} and there is no default value. Subclasses typically throw an exception in this case.
|
* returned {@code null} and there is no default value. Subclasses typically throw an exception in this case.
|
||||||
* @param name the name for the value
|
* @param name the name for the value
|
||||||
* @param parameter the method parameter
|
* @param parameter the method parameter
|
||||||
|
* @param request the current request
|
||||||
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
protected abstract void handleMissingValue(String name, MethodParameter parameter) throws ServletException;
|
protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
|
||||||
|
handleMissingValue(name, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a named value is required, but {@link #resolveName(String, MethodParameter, NativeWebRequest)}
|
||||||
|
* returned {@code null} and there is no default value. Subclasses typically throw an exception in this case.
|
||||||
|
* @param name the name for the value
|
||||||
|
* @param parameter the method parameter
|
||||||
|
*/
|
||||||
|
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
|
||||||
|
throw new ServletRequestBindingException("Missing argument '" + name +
|
||||||
|
"' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code null} results in a {@code false} value for {@code boolean}s or an exception for other primitives.
|
* A {@code null} results in a {@code false} value for {@code boolean}s or an exception for other primitives.
|
||||||
|
|
@ -202,7 +221,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||||
return Boolean.FALSE;
|
return Boolean.FALSE;
|
||||||
}
|
}
|
||||||
else if (paramType.isPrimitive()) {
|
else if (paramType.isPrimitive()) {
|
||||||
throw new IllegalStateException("Optional " + paramType + " parameter '" + name +
|
throw new IllegalStateException("Optional " + paramType.getSimpleName() + " parameter '" + name +
|
||||||
"' is present but cannot be translated into a null value due to being declared as a " +
|
"' is present but cannot be translated into a null value due to being declared as a " +
|
||||||
"primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
|
"primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
return (parameter.hasParameterAnnotation(RequestHeader.class) &&
|
return (parameter.hasParameterAnnotation(RequestHeader.class) &&
|
||||||
!Map.class.isAssignableFrom(parameter.getParameterType()));
|
!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -79,7 +79,7 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
|
||||||
@Override
|
@Override
|
||||||
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
|
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
|
||||||
throw new ServletRequestBindingException("Missing request header '" + name +
|
throw new ServletRequestBindingException("Missing request header '" + name +
|
||||||
"' for method parameter of type " + parameter.getParameterType().getSimpleName());
|
"' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,22 +17,17 @@
|
||||||
package org.springframework.web.method.annotation;
|
package org.springframework.web.method.annotation;
|
||||||
|
|
||||||
import java.beans.PropertyEditor;
|
import java.beans.PropertyEditor;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.Part;
|
|
||||||
|
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.core.GenericCollectionTypeResolver;
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
|
@ -45,6 +40,8 @@ import org.springframework.web.multipart.MultipartException;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||||
import org.springframework.web.multipart.MultipartResolver;
|
import org.springframework.web.multipart.MultipartResolver;
|
||||||
|
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||||
|
import org.springframework.web.multipart.support.MultipartResolutionDelegate;
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
import org.springframework.web.util.WebUtils;
|
import org.springframework.web.util.WebUtils;
|
||||||
|
|
||||||
|
|
@ -124,9 +121,8 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
Class<?> paramType = parameter.getParameterType();
|
|
||||||
if (parameter.hasParameterAnnotation(RequestParam.class)) {
|
if (parameter.hasParameterAnnotation(RequestParam.class)) {
|
||||||
if (Map.class.isAssignableFrom(paramType)) {
|
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
|
||||||
String paramName = parameter.getParameterAnnotation(RequestParam.class).name();
|
String paramName = parameter.getParameterAnnotation(RequestParam.class).name();
|
||||||
return StringUtils.hasText(paramName);
|
return StringUtils.hasText(paramName);
|
||||||
}
|
}
|
||||||
|
|
@ -138,11 +134,12 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
|
||||||
if (parameter.hasParameterAnnotation(RequestPart.class)) {
|
if (parameter.hasParameterAnnotation(RequestPart.class)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (MultipartFile.class == paramType || "javax.servlet.http.Part".equals(paramType.getName())) {
|
parameter = parameter.nestedIfOptional();
|
||||||
|
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (this.useDefaultResolution) {
|
else if (this.useDefaultResolution) {
|
||||||
return BeanUtils.isSimpleProperty(paramType);
|
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -157,112 +154,61 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
|
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
|
||||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
|
||||||
MultipartHttpServletRequest multipartRequest =
|
MultipartHttpServletRequest multipartRequest =
|
||||||
WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
|
WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
|
||||||
Object arg;
|
|
||||||
|
|
||||||
if (MultipartFile.class == parameter.getParameterType()) {
|
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
|
||||||
assertIsMultipartRequest(servletRequest);
|
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
|
||||||
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
|
return mpArg;
|
||||||
arg = multipartRequest.getFile(name);
|
|
||||||
}
|
|
||||||
else if (isMultipartFileCollection(parameter)) {
|
|
||||||
assertIsMultipartRequest(servletRequest);
|
|
||||||
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
|
|
||||||
arg = multipartRequest.getFiles(name);
|
|
||||||
}
|
|
||||||
else if (isMultipartFileArray(parameter)) {
|
|
||||||
assertIsMultipartRequest(servletRequest);
|
|
||||||
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
|
|
||||||
List<MultipartFile> multipartFiles = multipartRequest.getFiles(name);
|
|
||||||
arg = multipartFiles.toArray(new MultipartFile[multipartFiles.size()]);
|
|
||||||
}
|
|
||||||
else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {
|
|
||||||
assertIsMultipartRequest(servletRequest);
|
|
||||||
arg = servletRequest.getPart(name);
|
|
||||||
}
|
|
||||||
else if (isPartCollection(parameter)) {
|
|
||||||
assertIsMultipartRequest(servletRequest);
|
|
||||||
arg = new ArrayList<Object>(servletRequest.getParts());
|
|
||||||
}
|
|
||||||
else if (isPartArray(parameter)) {
|
|
||||||
assertIsMultipartRequest(servletRequest);
|
|
||||||
arg = RequestPartResolver.resolvePart(servletRequest);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
arg = null;
|
|
||||||
if (multipartRequest != null) {
|
|
||||||
List<MultipartFile> files = multipartRequest.getFiles(name);
|
|
||||||
if (!files.isEmpty()) {
|
|
||||||
arg = (files.size() == 1 ? files.get(0) : files);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (arg == null) {
|
|
||||||
String[] paramValues = webRequest.getParameterValues(name);
|
|
||||||
if (paramValues != null) {
|
|
||||||
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object arg = null;
|
||||||
|
if (multipartRequest != null) {
|
||||||
|
List<MultipartFile> files = multipartRequest.getFiles(name);
|
||||||
|
if (!files.isEmpty()) {
|
||||||
|
arg = (files.size() == 1 ? files.get(0) : files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arg == null) {
|
||||||
|
String[] paramValues = request.getParameterValues(name);
|
||||||
|
if (paramValues != null) {
|
||||||
|
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertIsMultipartRequest(HttpServletRequest request) {
|
@Override
|
||||||
String contentType = request.getContentType();
|
protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
|
||||||
if (contentType == null || !contentType.toLowerCase().startsWith("multipart/")) {
|
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
|
||||||
throw new MultipartException("The current request is not a multipart request");
|
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
|
||||||
}
|
if (!MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
|
||||||
}
|
throw new MultipartException("Current request is not a multipart request");
|
||||||
|
}
|
||||||
private boolean isMultipartFileCollection(MethodParameter parameter) {
|
else {
|
||||||
return (MultipartFile.class == getCollectionParameterType(parameter));
|
throw new MissingServletRequestPartException(name);
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMultipartFileArray(MethodParameter parameter) {
|
|
||||||
return (MultipartFile.class == parameter.getParameterType().getComponentType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPartCollection(MethodParameter parameter) {
|
|
||||||
Class<?> collectionType = getCollectionParameterType(parameter);
|
|
||||||
return (collectionType != null && "javax.servlet.http.Part".equals(collectionType.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPartArray(MethodParameter parameter) {
|
|
||||||
Class<?> paramType = parameter.getParameterType().getComponentType();
|
|
||||||
return (paramType != null && "javax.servlet.http.Part".equals(paramType.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Class<?> getCollectionParameterType(MethodParameter parameter) {
|
|
||||||
Class<?> paramType = parameter.getParameterType();
|
|
||||||
if (Collection.class == paramType || List.class.isAssignableFrom(paramType)){
|
|
||||||
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(parameter);
|
|
||||||
if (valueType != null) {
|
|
||||||
return valueType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
else {
|
||||||
}
|
throw new MissingServletRequestParameterException(name, parameter.getNestedParameterType().getSimpleName());
|
||||||
|
}
|
||||||
@Override
|
|
||||||
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
|
|
||||||
throw new MissingServletRequestParameterException(name, parameter.getParameterType().getSimpleName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contributeMethodArgument(MethodParameter parameter, Object value,
|
public void contributeMethodArgument(MethodParameter parameter, Object value,
|
||||||
UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) {
|
UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) {
|
||||||
|
|
||||||
Class<?> paramType = parameter.getParameterType();
|
Class<?> paramType = parameter.getNestedParameterType();
|
||||||
if (Map.class.isAssignableFrom(paramType) || MultipartFile.class == paramType ||
|
if (Map.class.isAssignableFrom(paramType) || MultipartFile.class == paramType ||
|
||||||
"javax.servlet.http.Part".equals(paramType.getName())) {
|
"javax.servlet.http.Part".equals(paramType.getName())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
|
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
|
||||||
String name = (requestParam == null || StringUtils.isEmpty(requestParam.name()) ? parameter.getParameterName() : requestParam.name());
|
String name = (requestParam == null || StringUtils.isEmpty(requestParam.name()) ?
|
||||||
|
parameter.getParameterName() : requestParam.name());
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
builder.queryParam(name);
|
builder.queryParam(name);
|
||||||
|
|
@ -305,12 +251,4 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class RequestPartResolver {
|
|
||||||
|
|
||||||
public static Object resolvePart(HttpServletRequest servletRequest) throws Exception {
|
|
||||||
return servletRequest.getParts().toArray(new Part[servletRequest.getParts().size()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -24,27 +24,28 @@ import org.springframework.web.multipart.MultipartResolver;
|
||||||
* Raised when the part of a "multipart/form-data" request identified by its
|
* Raised when the part of a "multipart/form-data" request identified by its
|
||||||
* name cannot be found.
|
* name cannot be found.
|
||||||
*
|
*
|
||||||
* <p>This may be because the request is not a multipart/form-data
|
* <p>This may be because the request is not a multipart/form-data request,
|
||||||
*
|
* because the part is not present in the request, or because the web
|
||||||
* either because the part is not present in the request, or
|
* application is not configured correctly for processing multipart requests,
|
||||||
* because the web application is not configured correctly for processing
|
* e.g. no {@link MultipartResolver}.
|
||||||
* multipart requests -- e.g. no {@link MultipartResolver}.
|
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
public class MissingServletRequestPartException extends ServletException {
|
public class MissingServletRequestPartException extends ServletException {
|
||||||
|
|
||||||
private static final long serialVersionUID = -1255077391966870705L;
|
|
||||||
|
|
||||||
private final String partName;
|
private final String partName;
|
||||||
|
|
||||||
|
|
||||||
public MissingServletRequestPartException(String partName) {
|
public MissingServletRequestPartException(String partName) {
|
||||||
super("Required request part '" + partName + "' is not present.");
|
super("Required request part '" + partName + "' is not present");
|
||||||
this.partName = partName;
|
this.partName = partName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getRequestPartName() {
|
public String getRequestPartName() {
|
||||||
return this.partName;
|
return this.partName;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
* 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.multipart.support;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.Part;
|
||||||
|
|
||||||
|
import org.springframework.core.GenericCollectionTypeResolver;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.web.multipart.MultipartException;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||||
|
import org.springframework.web.util.WebUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common delegate for {@code HandlerMethodArgumentResolver} implementations
|
||||||
|
* which need to resolve {@link MultipartFile} and {@link Part} arguments.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 4.3
|
||||||
|
*/
|
||||||
|
public abstract class MultipartResolutionDelegate {
|
||||||
|
|
||||||
|
public static final Object UNRESOLVABLE = new Object();
|
||||||
|
|
||||||
|
|
||||||
|
private static Class<?> servletPartClass = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
servletPartClass = ClassUtils.forName(
|
||||||
|
"javax.servlet.http.Part", MultipartResolutionDelegate.class.getClassLoader());
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ex) {
|
||||||
|
// Servlet 3.0 Part type not available - Part references simply not supported then.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isMultipartRequest(HttpServletRequest request) {
|
||||||
|
return (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null ||
|
||||||
|
isMultipartContent(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMultipartContent(HttpServletRequest request) {
|
||||||
|
String contentType = request.getContentType();
|
||||||
|
return (contentType != null && contentType.toLowerCase().startsWith("multipart/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static MultipartHttpServletRequest asMultipartHttpServletRequest(HttpServletRequest request) {
|
||||||
|
MultipartHttpServletRequest unwrapped = WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
|
||||||
|
if (unwrapped != null) {
|
||||||
|
return unwrapped;
|
||||||
|
}
|
||||||
|
return adaptToMultipartHttpServletRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MultipartHttpServletRequest adaptToMultipartHttpServletRequest(HttpServletRequest request) {
|
||||||
|
if (servletPartClass != null) {
|
||||||
|
// Servlet 3.0 available ..
|
||||||
|
return new StandardMultipartHttpServletRequest(request);
|
||||||
|
}
|
||||||
|
throw new MultipartException("Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isMultipartArgument(MethodParameter parameter) {
|
||||||
|
Class<?> paramType = parameter.getNestedParameterType();
|
||||||
|
return (MultipartFile.class == paramType || isMultipartFileCollection(parameter) ||
|
||||||
|
isMultipartFileArray(parameter) || servletPartClass == paramType ||
|
||||||
|
isPartCollection(parameter) || isPartArray(parameter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object resolveMultipartArgument(String name, MethodParameter parameter, HttpServletRequest request)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
MultipartHttpServletRequest multipartRequest =
|
||||||
|
WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
|
||||||
|
boolean isMultipart = (multipartRequest != null || isMultipartContent(request));
|
||||||
|
|
||||||
|
if (MultipartFile.class == parameter.getNestedParameterType()) {
|
||||||
|
if (multipartRequest == null && isMultipart) {
|
||||||
|
multipartRequest = adaptToMultipartHttpServletRequest(request);
|
||||||
|
}
|
||||||
|
return (multipartRequest != null ? multipartRequest.getFile(name) : null);
|
||||||
|
}
|
||||||
|
else if (isMultipartFileCollection(parameter)) {
|
||||||
|
if (multipartRequest == null && isMultipart) {
|
||||||
|
multipartRequest = adaptToMultipartHttpServletRequest(request);
|
||||||
|
}
|
||||||
|
return (multipartRequest != null ? multipartRequest.getFiles(name) : null);
|
||||||
|
}
|
||||||
|
else if (isMultipartFileArray(parameter)) {
|
||||||
|
if (multipartRequest == null && isMultipart) {
|
||||||
|
multipartRequest = adaptToMultipartHttpServletRequest(request);
|
||||||
|
}
|
||||||
|
if (multipartRequest != null) {
|
||||||
|
List<MultipartFile> multipartFiles = multipartRequest.getFiles(name);
|
||||||
|
return multipartFiles.toArray(new MultipartFile[multipartFiles.size()]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parameter.getNestedParameterType() == servletPartClass) {
|
||||||
|
return (isMultipart ? RequestPartResolver.resolvePart(request, name) : null);
|
||||||
|
}
|
||||||
|
else if (isPartCollection(parameter)) {
|
||||||
|
return (isMultipart ? RequestPartResolver.resolvePartList(request, name) : null);
|
||||||
|
}
|
||||||
|
else if (isPartArray(parameter)) {
|
||||||
|
return (isMultipart ? RequestPartResolver.resolvePartArray(request, name) : null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return UNRESOLVABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMultipartFileCollection(MethodParameter methodParam) {
|
||||||
|
return (MultipartFile.class == getCollectionParameterType(methodParam));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMultipartFileArray(MethodParameter methodParam) {
|
||||||
|
return (MultipartFile.class == methodParam.getNestedParameterType().getComponentType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPartCollection(MethodParameter methodParam) {
|
||||||
|
return (servletPartClass == getCollectionParameterType(methodParam));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPartArray(MethodParameter methodParam) {
|
||||||
|
return (servletPartClass == methodParam.getNestedParameterType().getComponentType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Class<?> getCollectionParameterType(MethodParameter methodParam) {
|
||||||
|
Class<?> paramType = methodParam.getNestedParameterType();
|
||||||
|
if (Collection.class == paramType || List.class.isAssignableFrom(paramType)){
|
||||||
|
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
|
||||||
|
if (valueType != null) {
|
||||||
|
return valueType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner class to avoid hard-coded dependency on Servlet 3.0 Part type...
|
||||||
|
*/
|
||||||
|
private static class RequestPartResolver {
|
||||||
|
|
||||||
|
public static Object resolvePart(HttpServletRequest servletRequest, String name) throws Exception {
|
||||||
|
return servletRequest.getPart(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object resolvePartList(HttpServletRequest servletRequest, String name) throws Exception {
|
||||||
|
Collection<Part> parts = servletRequest.getParts();
|
||||||
|
List<Part> result = new ArrayList<Part>(parts.size());
|
||||||
|
for (Part part : parts) {
|
||||||
|
if (part.getName().equals(name)) {
|
||||||
|
result.add(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object resolvePartArray(HttpServletRequest servletRequest, String name) throws Exception {
|
||||||
|
Collection<Part> parts = servletRequest.getParts();
|
||||||
|
List<Part> result = new ArrayList<Part>(parts.size());
|
||||||
|
for (Part part : parts) {
|
||||||
|
if (part.getName().equals(name)) {
|
||||||
|
result.add(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toArray(new Part[result.size()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -26,13 +26,10 @@ import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.server.ServerHttpRequest;
|
import org.springframework.http.server.ServerHttpRequest;
|
||||||
import org.springframework.http.server.ServletServerHttpRequest;
|
import org.springframework.http.server.ServletServerHttpRequest;
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
import org.springframework.web.multipart.MultipartException;
|
import org.springframework.web.multipart.MultipartException;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||||
import org.springframework.web.multipart.MultipartResolver;
|
import org.springframework.web.multipart.MultipartResolver;
|
||||||
import org.springframework.web.util.WebUtils;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ServerHttpRequest} implementation that accesses one part of a multipart
|
* {@link ServerHttpRequest} implementation that accesses one part of a multipart
|
||||||
|
|
@ -54,46 +51,26 @@ public class RequestPartServletServerHttpRequest extends ServletServerHttpReques
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance.
|
* Create a new {@code RequestPartServletServerHttpRequest} instance.
|
||||||
* @param request the current request
|
* @param request the current servlet request
|
||||||
* @param partName the name of the part to adapt to the {@link ServerHttpRequest} contract
|
* @param partName the name of the part to adapt to the {@link ServerHttpRequest} contract
|
||||||
* @throws MissingServletRequestPartException if the request part cannot be found
|
* @throws MissingServletRequestPartException if the request part cannot be found
|
||||||
* @throws IllegalArgumentException if MultipartHttpServletRequest cannot be initialized
|
* @throws MultipartException if MultipartHttpServletRequest cannot be initialized
|
||||||
*/
|
*/
|
||||||
public RequestPartServletServerHttpRequest(HttpServletRequest request, String partName)
|
public RequestPartServletServerHttpRequest(HttpServletRequest request, String partName)
|
||||||
throws MissingServletRequestPartException {
|
throws MissingServletRequestPartException {
|
||||||
|
|
||||||
super(request);
|
super(request);
|
||||||
|
|
||||||
this.multipartRequest = asMultipartRequest(request);
|
this.multipartRequest = MultipartResolutionDelegate.asMultipartHttpServletRequest(request);
|
||||||
this.partName = partName;
|
this.partName = partName;
|
||||||
|
|
||||||
this.headers = this.multipartRequest.getMultipartHeaders(this.partName);
|
this.headers = this.multipartRequest.getMultipartHeaders(this.partName);
|
||||||
if (this.headers == null) {
|
if (this.headers == null) {
|
||||||
if (request instanceof MultipartHttpServletRequest) {
|
throw new MissingServletRequestPartException(partName);
|
||||||
throw new MissingServletRequestPartException(partName);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Failed to obtain request part: " + partName + ". " +
|
|
||||||
"The part is missing or multipart processing is not configured. " +
|
|
||||||
"Check for a MultipartResolver bean or if Servlet 3.0 multipart processing is enabled.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MultipartHttpServletRequest asMultipartRequest(HttpServletRequest request) {
|
|
||||||
MultipartHttpServletRequest unwrapped = WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
|
|
||||||
if (unwrapped != null) {
|
|
||||||
return unwrapped;
|
|
||||||
}
|
|
||||||
else if (ClassUtils.hasMethod(HttpServletRequest.class, "getParts")) {
|
|
||||||
// Servlet 3.0 available ..
|
|
||||||
return new StandardMultipartHttpServletRequest(request);
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpHeaders getHeaders() {
|
public HttpHeaders getHeaders() {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -37,6 +37,7 @@ import org.springframework.mock.web.test.MockHttpServletResponse;
|
||||||
import org.springframework.mock.web.test.MockMultipartFile;
|
import org.springframework.mock.web.test.MockMultipartFile;
|
||||||
import org.springframework.mock.web.test.MockMultipartHttpServletRequest;
|
import org.springframework.mock.web.test.MockMultipartHttpServletRequest;
|
||||||
import org.springframework.mock.web.test.MockPart;
|
import org.springframework.mock.web.test.MockPart;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
@ -49,6 +50,7 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.context.request.ServletWebRequest;
|
import org.springframework.web.context.request.ServletWebRequest;
|
||||||
import org.springframework.web.multipart.MultipartException;
|
import org.springframework.web.multipart.MultipartException;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static org.mockito.BDDMockito.*;
|
import static org.mockito.BDDMockito.*;
|
||||||
|
|
@ -82,22 +84,18 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
private MethodParameter paramRequired;
|
private MethodParameter paramRequired;
|
||||||
private MethodParameter paramNotRequired;
|
private MethodParameter paramNotRequired;
|
||||||
private MethodParameter paramOptional;
|
private MethodParameter paramOptional;
|
||||||
|
private MethodParameter multipartFileOptional;
|
||||||
|
|
||||||
private NativeWebRequest webRequest;
|
private NativeWebRequest webRequest;
|
||||||
|
|
||||||
private MockHttpServletRequest request;
|
private MockHttpServletRequest request;
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
resolver = new RequestParamMethodArgumentResolver(null, true);
|
resolver = new RequestParamMethodArgumentResolver(null, true);
|
||||||
|
|
||||||
ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||||
|
Method method = ReflectionUtils.findMethod(getClass(), "handle", (Class<?>[]) null);
|
||||||
Method method = getClass().getMethod("params", String.class, String[].class,
|
|
||||||
Map.class, MultipartFile.class, List.class, MultipartFile[].class,
|
|
||||||
Part.class, List.class, Part[].class, Map.class,
|
|
||||||
String.class, MultipartFile.class, List.class, Part.class,
|
|
||||||
MultipartFile.class, String.class, String.class, Optional.class);
|
|
||||||
|
|
||||||
paramNamedDefaultValueString = new SynthesizingMethodParameter(method, 0);
|
paramNamedDefaultValueString = new SynthesizingMethodParameter(method, 0);
|
||||||
paramNamedStringArray = new SynthesizingMethodParameter(method, 1);
|
paramNamedStringArray = new SynthesizingMethodParameter(method, 1);
|
||||||
|
|
@ -121,27 +119,35 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
paramRequired = new SynthesizingMethodParameter(method, 15);
|
paramRequired = new SynthesizingMethodParameter(method, 15);
|
||||||
paramNotRequired = new SynthesizingMethodParameter(method, 16);
|
paramNotRequired = new SynthesizingMethodParameter(method, 16);
|
||||||
paramOptional = new SynthesizingMethodParameter(method, 17);
|
paramOptional = new SynthesizingMethodParameter(method, 17);
|
||||||
|
multipartFileOptional = new SynthesizingMethodParameter(method, 18);
|
||||||
|
|
||||||
request = new MockHttpServletRequest();
|
request = new MockHttpServletRequest();
|
||||||
webRequest = new ServletWebRequest(request, new MockHttpServletResponse());
|
webRequest = new ServletWebRequest(request, new MockHttpServletResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void supportsParameter() {
|
public void supportsParameter() {
|
||||||
resolver = new RequestParamMethodArgumentResolver(null, true);
|
resolver = new RequestParamMethodArgumentResolver(null, true);
|
||||||
assertTrue("String parameter not supported", resolver.supportsParameter(paramNamedDefaultValueString));
|
assertTrue(resolver.supportsParameter(paramNamedDefaultValueString));
|
||||||
assertTrue("String array parameter not supported", resolver.supportsParameter(paramNamedStringArray));
|
assertTrue(resolver.supportsParameter(paramNamedStringArray));
|
||||||
assertTrue("Named map not parameter supported", resolver.supportsParameter(paramNamedMap));
|
assertTrue(resolver.supportsParameter(paramNamedMap));
|
||||||
assertTrue("MultipartFile parameter not supported", resolver.supportsParameter(paramMultipartFile));
|
assertTrue(resolver.supportsParameter(paramMultipartFile));
|
||||||
assertTrue("List<MultipartFile> parameter not supported", resolver.supportsParameter(paramMultipartFileList));
|
assertTrue(resolver.supportsParameter(paramMultipartFileList));
|
||||||
assertTrue("MultipartFile[] parameter not supported", resolver.supportsParameter(paramMultipartFileArray));
|
assertTrue(resolver.supportsParameter(paramMultipartFileArray));
|
||||||
assertTrue("Part parameter not supported", resolver.supportsParameter(paramPart));
|
assertTrue(resolver.supportsParameter(paramPart));
|
||||||
assertTrue("List<Part> parameter not supported", resolver.supportsParameter(paramPartList));
|
assertTrue(resolver.supportsParameter(paramPartList));
|
||||||
assertTrue("Part[] parameter not supported", resolver.supportsParameter(paramPartArray));
|
assertTrue(resolver.supportsParameter(paramPartArray));
|
||||||
assertFalse("non-@RequestParam parameter supported", resolver.supportsParameter(paramMap));
|
assertFalse(resolver.supportsParameter(paramMap));
|
||||||
assertTrue("Simple type params supported w/o annotations", resolver.supportsParameter(paramStringNotAnnot));
|
assertTrue(resolver.supportsParameter(paramStringNotAnnot));
|
||||||
assertTrue("MultipartFile parameter not supported", resolver.supportsParameter(paramMultipartFileNotAnnot));
|
assertTrue(resolver.supportsParameter(paramMultipartFileNotAnnot));
|
||||||
assertTrue("Part parameter not supported", resolver.supportsParameter(paramPartNotAnnot));
|
assertTrue(resolver.supportsParameter(paramMultipartFileListNotAnnot));
|
||||||
|
assertTrue(resolver.supportsParameter(paramPartNotAnnot));
|
||||||
|
assertFalse(resolver.supportsParameter(paramRequestPartAnnot));
|
||||||
|
assertTrue(resolver.supportsParameter(paramRequired));
|
||||||
|
assertTrue(resolver.supportsParameter(paramNotRequired));
|
||||||
|
assertTrue(resolver.supportsParameter(paramOptional));
|
||||||
|
assertTrue(resolver.supportsParameter(multipartFileOptional));
|
||||||
|
|
||||||
resolver = new RequestParamMethodArgumentResolver(null, false);
|
resolver = new RequestParamMethodArgumentResolver(null, false);
|
||||||
assertFalse(resolver.supportsParameter(paramStringNotAnnot));
|
assertFalse(resolver.supportsParameter(paramStringNotAnnot));
|
||||||
|
|
@ -154,18 +160,16 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
request.addParameter("name", expected);
|
request.addParameter("name", expected);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramNamedDefaultValueString, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramNamedDefaultValueString, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof String);
|
assertTrue(result instanceof String);
|
||||||
assertEquals("Invalid result", expected, result);
|
assertEquals("Invalid result", expected, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveStringArray() throws Exception {
|
public void resolveStringArray() throws Exception {
|
||||||
String[] expected = new String[]{"foo", "bar"};
|
String[] expected = new String[] {"foo", "bar"};
|
||||||
request.addParameter("name", expected);
|
request.addParameter("name", expected);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramNamedStringArray, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramNamedStringArray, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof String[]);
|
assertTrue(result instanceof String[]);
|
||||||
assertArrayEquals("Invalid result", expected, (String[]) result);
|
assertArrayEquals("Invalid result", expected, (String[]) result);
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +182,6 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof MultipartFile);
|
assertTrue(result instanceof MultipartFile);
|
||||||
assertEquals("Invalid result", expected, result);
|
assertEquals("Invalid result", expected, result);
|
||||||
}
|
}
|
||||||
|
|
@ -190,10 +193,10 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
MultipartFile expected2 = new MockMultipartFile("mfilelist", "Hello World 2".getBytes());
|
MultipartFile expected2 = new MockMultipartFile("mfilelist", "Hello World 2".getBytes());
|
||||||
request.addFile(expected1);
|
request.addFile(expected1);
|
||||||
request.addFile(expected2);
|
request.addFile(expected2);
|
||||||
|
request.addFile(new MockMultipartFile("other", "Hello World 3".getBytes()));
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramMultipartFileList, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramMultipartFileList, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof List);
|
assertTrue(result instanceof List);
|
||||||
assertEquals(Arrays.asList(expected1, expected2), result);
|
assertEquals(Arrays.asList(expected1, expected2), result);
|
||||||
}
|
}
|
||||||
|
|
@ -205,12 +208,13 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
MultipartFile expected2 = new MockMultipartFile("mfilearray", "Hello World 2".getBytes());
|
MultipartFile expected2 = new MockMultipartFile("mfilearray", "Hello World 2".getBytes());
|
||||||
request.addFile(expected1);
|
request.addFile(expected1);
|
||||||
request.addFile(expected2);
|
request.addFile(expected2);
|
||||||
|
request.addFile(new MockMultipartFile("other", "Hello World 3".getBytes()));
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramMultipartFileArray, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramMultipartFileArray, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof MultipartFile[]);
|
assertTrue(result instanceof MultipartFile[]);
|
||||||
MultipartFile[] parts = (MultipartFile[]) result;
|
MultipartFile[] parts = (MultipartFile[]) result;
|
||||||
|
assertEquals(2, parts.length);
|
||||||
assertEquals(parts[0], expected1);
|
assertEquals(parts[0], expected1);
|
||||||
assertEquals(parts[1], expected2);
|
assertEquals(parts[1], expected2);
|
||||||
}
|
}
|
||||||
|
|
@ -225,7 +229,6 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramPart, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramPart, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof Part);
|
assertTrue(result instanceof Part);
|
||||||
assertEquals("Invalid result", expected, result);
|
assertEquals("Invalid result", expected, result);
|
||||||
}
|
}
|
||||||
|
|
@ -233,16 +236,16 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
@Test
|
@Test
|
||||||
public void resolvePartList() throws Exception {
|
public void resolvePartList() throws Exception {
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
MockPart expected1 = new MockPart("pfilelist", "Hello World 1".getBytes());
|
|
||||||
MockPart expected2 = new MockPart("pfilelist", "Hello World 2".getBytes());
|
|
||||||
request.setMethod("POST");
|
request.setMethod("POST");
|
||||||
request.setContentType("multipart/form-data");
|
request.setContentType("multipart/form-data");
|
||||||
|
MockPart expected1 = new MockPart("pfilelist", "Hello World 1".getBytes());
|
||||||
|
MockPart expected2 = new MockPart("pfilelist", "Hello World 2".getBytes());
|
||||||
request.addPart(expected1);
|
request.addPart(expected1);
|
||||||
request.addPart(expected2);
|
request.addPart(expected2);
|
||||||
|
request.addPart(new MockPart("other", "Hello World 3".getBytes()));
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramPartList, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramPartList, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof List);
|
assertTrue(result instanceof List);
|
||||||
assertEquals(Arrays.asList(expected1, expected2), result);
|
assertEquals(Arrays.asList(expected1, expected2), result);
|
||||||
}
|
}
|
||||||
|
|
@ -256,12 +259,13 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
request.setContentType("multipart/form-data");
|
request.setContentType("multipart/form-data");
|
||||||
request.addPart(expected1);
|
request.addPart(expected1);
|
||||||
request.addPart(expected2);
|
request.addPart(expected2);
|
||||||
|
request.addPart(new MockPart("other", "Hello World 3".getBytes()));
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramPartArray, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramPartArray, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof Part[]);
|
assertTrue(result instanceof Part[]);
|
||||||
Part[] parts = (Part[]) result;
|
Part[] parts = (Part[]) result;
|
||||||
|
assertEquals(2, parts.length);
|
||||||
assertEquals(parts[0], expected1);
|
assertEquals(parts[0], expected1);
|
||||||
assertEquals(parts[1], expected2);
|
assertEquals(parts[1], expected2);
|
||||||
}
|
}
|
||||||
|
|
@ -274,7 +278,6 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramMultipartFileNotAnnot, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramMultipartFileNotAnnot, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof MultipartFile);
|
assertTrue(result instanceof MultipartFile);
|
||||||
assertEquals("Invalid result", expected, result);
|
assertEquals("Invalid result", expected, result);
|
||||||
}
|
}
|
||||||
|
|
@ -289,7 +292,6 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramMultipartFileListNotAnnot, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramMultipartFileListNotAnnot, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof List);
|
assertTrue(result instanceof List);
|
||||||
assertEquals(Arrays.asList(expected1, expected2), result);
|
assertEquals(Arrays.asList(expected1, expected2), result);
|
||||||
}
|
}
|
||||||
|
|
@ -300,9 +302,7 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
fail("Expected exception: request is not a multipart request");
|
fail("Expected exception: request is not a multipart request");
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPR-9079
|
@Test // SPR-9079
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isMultipartRequestHttpPut() throws Exception {
|
public void isMultipartRequestHttpPut() throws Exception {
|
||||||
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
||||||
MultipartFile expected = new MockMultipartFile("multipartFileList", "Hello World".getBytes());
|
MultipartFile expected = new MockMultipartFile("multipartFileList", "Hello World".getBytes());
|
||||||
|
|
@ -311,17 +311,23 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object actual = resolver.resolveArgument(paramMultipartFileListNotAnnot, null, webRequest, null);
|
Object actual = resolver.resolveArgument(paramMultipartFileListNotAnnot, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(actual instanceof List);
|
assertTrue(actual instanceof List);
|
||||||
assertEquals(expected, ((List<?>) actual).get(0));
|
assertEquals(expected, ((List<?>) actual).get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = MultipartException.class)
|
||||||
|
public void noMultipartContent() throws Exception {
|
||||||
|
request.setMethod("POST");
|
||||||
|
resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
|
||||||
|
fail("Expected exception: no multipart content");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = MissingServletRequestPartException.class)
|
||||||
public void missingMultipartFile() throws Exception {
|
public void missingMultipartFile() throws Exception {
|
||||||
request.setMethod("POST");
|
request.setMethod("POST");
|
||||||
request.setContentType("multipart/form-data");
|
request.setContentType("multipart/form-data");
|
||||||
resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
|
resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
|
||||||
fail("Expected exception: request is not MultiPartHttpServletRequest but param is MultipartFile");
|
fail("Expected exception: no such part found");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -334,7 +340,6 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramPartNotAnnot, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramPartNotAnnot, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof Part);
|
assertTrue(result instanceof Part);
|
||||||
assertEquals("Invalid result", expected, result);
|
assertEquals("Invalid result", expected, result);
|
||||||
}
|
}
|
||||||
|
|
@ -342,7 +347,6 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
@Test
|
@Test
|
||||||
public void resolveDefaultValue() throws Exception {
|
public void resolveDefaultValue() throws Exception {
|
||||||
Object result = resolver.resolveArgument(paramNamedDefaultValueString, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramNamedDefaultValueString, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof String);
|
assertTrue(result instanceof String);
|
||||||
assertEquals("Invalid result", "bar", result);
|
assertEquals("Invalid result", "bar", result);
|
||||||
}
|
}
|
||||||
|
|
@ -353,11 +357,8 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
fail("Expected exception");
|
fail("Expected exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPR-10578
|
@Test // SPR-10578
|
||||||
|
|
||||||
@Test
|
|
||||||
public void missingRequestParamEmptyValueConvertedToNull() throws Exception {
|
public void missingRequestParamEmptyValueConvertedToNull() throws Exception {
|
||||||
|
|
||||||
WebDataBinder binder = new WebRequestDataBinder(null);
|
WebDataBinder binder = new WebRequestDataBinder(null);
|
||||||
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
|
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
|
||||||
|
|
||||||
|
|
@ -367,13 +368,11 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
this.request.addParameter("stringNotAnnot", "");
|
this.request.addParameter("stringNotAnnot", "");
|
||||||
|
|
||||||
Object arg = resolver.resolveArgument(paramStringNotAnnot, null, webRequest, binderFactory);
|
Object arg = resolver.resolveArgument(paramStringNotAnnot, null, webRequest, binderFactory);
|
||||||
|
|
||||||
assertNull(arg);
|
assertNull(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void missingRequestParamEmptyValueNotRequired() throws Exception {
|
public void missingRequestParamEmptyValueNotRequired() throws Exception {
|
||||||
|
|
||||||
WebDataBinder binder = new WebRequestDataBinder(null);
|
WebDataBinder binder = new WebRequestDataBinder(null);
|
||||||
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
|
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
|
||||||
|
|
||||||
|
|
@ -383,7 +382,6 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
this.request.addParameter("name", "");
|
this.request.addParameter("name", "");
|
||||||
|
|
||||||
Object arg = resolver.resolveArgument(paramNotRequired, null, webRequest, binderFactory);
|
Object arg = resolver.resolveArgument(paramNotRequired, null, webRequest, binderFactory);
|
||||||
|
|
||||||
assertNull(arg);
|
assertNull(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -396,17 +394,13 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
assertEquals("plainValue", result);
|
assertEquals("plainValue", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPR-8561
|
@Test // SPR-8561
|
||||||
|
|
||||||
@Test
|
|
||||||
public void resolveSimpleTypeParamToNull() throws Exception {
|
public void resolveSimpleTypeParamToNull() throws Exception {
|
||||||
Object result = resolver.resolveArgument(paramStringNotAnnot, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramStringNotAnnot, null, webRequest, null);
|
||||||
assertNull(result);
|
assertNull(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPR-10180
|
@Test // SPR-10180
|
||||||
|
|
||||||
@Test
|
|
||||||
public void resolveEmptyValueToDefault() throws Exception {
|
public void resolveEmptyValueToDefault() throws Exception {
|
||||||
this.request.addParameter("name", "");
|
this.request.addParameter("name", "");
|
||||||
Object result = resolver.resolveArgument(paramNamedDefaultValueString, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramNamedDefaultValueString, null, webRequest, null);
|
||||||
|
|
@ -429,13 +423,12 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public void resolveOptional() throws Exception {
|
public void resolveOptionalParamValue() throws Exception {
|
||||||
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
|
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
|
||||||
initializer.setConversionService(new DefaultConversionService());
|
initializer.setConversionService(new DefaultConversionService());
|
||||||
WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer);
|
WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramOptional, null, webRequest, binderFactory);
|
Object result = resolver.resolveArgument(paramOptional, null, webRequest, binderFactory);
|
||||||
assertEquals(Optional.class, result.getClass());
|
|
||||||
assertEquals(Optional.empty(), result);
|
assertEquals(Optional.empty(), result);
|
||||||
|
|
||||||
this.request.addParameter("name", "123");
|
this.request.addParameter("name", "123");
|
||||||
|
|
@ -444,8 +437,46 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
assertEquals(123, ((Optional) result).get());
|
assertEquals(123, ((Optional) result).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveOptionalMultipartFile() throws Exception {
|
||||||
|
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
|
||||||
|
initializer.setConversionService(new DefaultConversionService());
|
||||||
|
WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer);
|
||||||
|
|
||||||
public void params(@RequestParam(name = "name", defaultValue = "bar") String param1,
|
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
||||||
|
MultipartFile expected = new MockMultipartFile("mfile", "Hello World".getBytes());
|
||||||
|
request.addFile(expected);
|
||||||
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
|
Object result = resolver.resolveArgument(multipartFileOptional, null, webRequest, binderFactory);
|
||||||
|
assertTrue(result instanceof Optional);
|
||||||
|
assertEquals("Invalid result", expected, ((Optional<?>) result).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void missingOptionalMultipartFile() throws Exception {
|
||||||
|
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
|
||||||
|
initializer.setConversionService(new DefaultConversionService());
|
||||||
|
WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer);
|
||||||
|
|
||||||
|
request.setMethod("POST");
|
||||||
|
request.setContentType("multipart/form-data");
|
||||||
|
assertEquals(Optional.empty(), resolver.resolveArgument(multipartFileOptional, null, webRequest, binderFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void optionalMultipartFileWithoutMultipartRequest() throws Exception {
|
||||||
|
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
|
||||||
|
initializer.setConversionService(new DefaultConversionService());
|
||||||
|
WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer);
|
||||||
|
|
||||||
|
assertEquals(Optional.empty(), resolver.resolveArgument(multipartFileOptional, null, webRequest, binderFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public void handle(
|
||||||
|
@RequestParam(name = "name", defaultValue = "bar") String param1,
|
||||||
@RequestParam("name") String[] param2,
|
@RequestParam("name") String[] param2,
|
||||||
@RequestParam("name") Map<?, ?> param3,
|
@RequestParam("name") Map<?, ?> param3,
|
||||||
@RequestParam("mfile") MultipartFile param4,
|
@RequestParam("mfile") MultipartFile param4,
|
||||||
|
|
@ -462,7 +493,8 @@ public class RequestParamMethodArgumentResolverTests {
|
||||||
@RequestPart MultipartFile requestPartAnnot,
|
@RequestPart MultipartFile requestPartAnnot,
|
||||||
@RequestParam("name") String paramRequired,
|
@RequestParam("name") String paramRequired,
|
||||||
@RequestParam(name = "name", required = false) String paramNotRequired,
|
@RequestParam(name = "name", required = false) String paramNotRequired,
|
||||||
@RequestParam("name") Optional<Integer> paramOptional) {
|
@RequestParam("name") Optional<Integer> paramOptional,
|
||||||
|
@RequestParam("mfile") Optional<MultipartFile> multipartFileOptional) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -48,12 +48,13 @@ public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMeth
|
||||||
super(null);
|
super(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
if (!parameter.hasParameterAnnotation(MatrixVariable.class)) {
|
if (!parameter.hasParameterAnnotation(MatrixVariable.class)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Map.class.isAssignableFrom(parameter.getParameterType())) {
|
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
|
||||||
String variableName = parameter.getParameterAnnotation(MatrixVariable.class).name();
|
String variableName = parameter.getParameterAnnotation(MatrixVariable.class).name();
|
||||||
return StringUtils.hasText(variableName);
|
return StringUtils.hasText(variableName);
|
||||||
}
|
}
|
||||||
|
|
@ -67,13 +68,10 @@ public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMeth
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
|
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
|
||||||
|
Map<String, MultiValueMap<String, String>> pathParameters = (Map<String, MultiValueMap<String, String>>)
|
||||||
@SuppressWarnings("unchecked")
|
request.getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
||||||
Map<String, MultiValueMap<String, String>> pathParameters =
|
|
||||||
(Map<String, MultiValueMap<String, String>>) request.getAttribute(
|
|
||||||
HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(pathParameters)) {
|
if (CollectionUtils.isEmpty(pathParameters)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -92,10 +90,10 @@ public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMeth
|
||||||
for (MultiValueMap<String, String> params : pathParameters.values()) {
|
for (MultiValueMap<String, String> params : pathParameters.values()) {
|
||||||
if (params.containsKey(name)) {
|
if (params.containsKey(name)) {
|
||||||
if (found) {
|
if (found) {
|
||||||
String paramType = parameter.getParameterType().getName();
|
String paramType = parameter.getNestedParameterType().getName();
|
||||||
throw new ServletRequestBindingException(
|
throw new ServletRequestBindingException(
|
||||||
"Found more than one match for URI path parameter '" + name +
|
"Found more than one match for URI path parameter '" + name +
|
||||||
"' for parameter type [" + paramType + "]. Use pathVar attribute to disambiguate.");
|
"' for parameter type [" + paramType + "]. Use 'pathVar' attribute to disambiguate.");
|
||||||
}
|
}
|
||||||
paramValues.addAll(params.get(name));
|
paramValues.addAll(params.get(name));
|
||||||
found = true;
|
found = true;
|
||||||
|
|
@ -117,7 +115,7 @@ public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMeth
|
||||||
@Override
|
@Override
|
||||||
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
|
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
|
||||||
throw new ServletRequestBindingException("Missing matrix variable '" + name +
|
throw new ServletRequestBindingException("Missing matrix variable '" + name +
|
||||||
"' for method parameter of type " + parameter.getParameterType().getSimpleName());
|
"' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -127,4 +125,5 @@ public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMeth
|
||||||
super(annotation.name(), annotation.required(), annotation.defaultValue());
|
super(annotation.name(), annotation.required(), annotation.defaultValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +79,7 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod
|
||||||
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
|
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Map.class.isAssignableFrom(parameter.getParameterType())) {
|
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
|
||||||
String paramName = parameter.getParameterAnnotation(PathVariable.class).value();
|
String paramName = parameter.getParameterAnnotation(PathVariable.class).value();
|
||||||
return StringUtils.hasText(paramName);
|
return StringUtils.hasText(paramName);
|
||||||
}
|
}
|
||||||
|
|
@ -95,10 +95,9 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
|
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
|
||||||
Map<String, String> uriTemplateVars =
|
Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
|
||||||
(Map<String, String>) request.getAttribute(
|
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
||||||
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
|
return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
|
||||||
return (uriTemplateVars != null) ? uriTemplateVars.get(name) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -127,7 +126,7 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod
|
||||||
public void contributeMethodArgument(MethodParameter parameter, Object value,
|
public void contributeMethodArgument(MethodParameter parameter, Object value,
|
||||||
UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) {
|
UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) {
|
||||||
|
|
||||||
if (Map.class.isAssignableFrom(parameter.getParameterType())) {
|
if (Map.class.isAssignableFrom(parameter.getNestedParameterType())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,20 +16,15 @@
|
||||||
|
|
||||||
package org.springframework.web.servlet.mvc.method.annotation;
|
package org.springframework.web.servlet.mvc.method.annotation;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.Part;
|
|
||||||
|
|
||||||
import org.springframework.core.GenericCollectionTypeResolver;
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.http.HttpInputMessage;
|
import org.springframework.http.HttpInputMessage;
|
||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.lang.UsesJava8;
|
import org.springframework.lang.UsesJava8;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
|
@ -41,12 +36,10 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
import org.springframework.web.multipart.MultipartException;
|
import org.springframework.web.multipart.MultipartException;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
|
||||||
import org.springframework.web.multipart.MultipartResolver;
|
import org.springframework.web.multipart.MultipartResolver;
|
||||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||||
|
import org.springframework.web.multipart.support.MultipartResolutionDelegate;
|
||||||
import org.springframework.web.multipart.support.RequestPartServletServerHttpRequest;
|
import org.springframework.web.multipart.support.RequestPartServletServerHttpRequest;
|
||||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
|
||||||
import org.springframework.web.util.WebUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the following method arguments:
|
* Resolves the following method arguments:
|
||||||
|
|
@ -65,12 +58,13 @@ import org.springframework.web.util.WebUtils;
|
||||||
* it is derived from the name of the method argument.
|
* it is derived from the name of the method argument.
|
||||||
*
|
*
|
||||||
* <p>Automatic validation may be applied if the argument is annotated with
|
* <p>Automatic validation may be applied if the argument is annotated with
|
||||||
* {@code @javax.validation.Valid}. In case of validation failure, a
|
* {@code @javax.validation.Valid}. In case of validation failure, a {@link MethodArgumentNotValidException}
|
||||||
* {@link MethodArgumentNotValidException} is raised and a 400 response status
|
* is raised and a 400 response status code returned if
|
||||||
* code returned if {@link DefaultHandlerExceptionResolver} is configured.
|
* {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver} is configured.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
public class RequestPartMethodArgumentResolver extends AbstractMessageConverterMethodArgumentResolver {
|
public class RequestPartMethodArgumentResolver extends AbstractMessageConverterMethodArgumentResolver {
|
||||||
|
|
@ -107,18 +101,10 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (parameter.hasParameterAnnotation(RequestParam.class)){
|
if (parameter.hasParameterAnnotation(RequestParam.class)) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (MultipartFile.class == parameter.getParameterType()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return MultipartResolutionDelegate.isMultipartArgument(parameter.nestedIfOptional());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,87 +114,58 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||||
NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
|
NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
|
||||||
|
|
||||||
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
|
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
|
||||||
assertIsMultipartRequest(servletRequest);
|
RequestPart requestPart = parameter.getParameterAnnotation(RequestPart.class);
|
||||||
|
boolean isRequired = ((requestPart == null || requestPart.required()) && !parameter.isOptional());
|
||||||
|
|
||||||
MultipartHttpServletRequest multipartRequest =
|
String name = getPartName(parameter, requestPart);
|
||||||
WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
|
parameter = parameter.nestedIfOptional();
|
||||||
|
Object arg = null;
|
||||||
|
|
||||||
Class<?> paramType = parameter.getParameterType();
|
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
|
||||||
boolean optional = paramType.getName().equals("java.util.Optional");
|
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
|
||||||
if (optional) {
|
arg = mpArg;
|
||||||
parameter.increaseNestingLevel();
|
|
||||||
paramType = parameter.getNestedParameterType();
|
|
||||||
}
|
|
||||||
|
|
||||||
String partName = getPartName(parameter);
|
|
||||||
Object arg;
|
|
||||||
|
|
||||||
if (MultipartFile.class == paramType) {
|
|
||||||
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
|
|
||||||
arg = multipartRequest.getFile(partName);
|
|
||||||
}
|
|
||||||
else if (isMultipartFileCollection(parameter)) {
|
|
||||||
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
|
|
||||||
arg = multipartRequest.getFiles(partName);
|
|
||||||
}
|
|
||||||
else if (isMultipartFileArray(parameter)) {
|
|
||||||
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
|
|
||||||
List<MultipartFile> files = multipartRequest.getFiles(partName);
|
|
||||||
arg = files.toArray(new MultipartFile[files.size()]);
|
|
||||||
}
|
|
||||||
else if ("javax.servlet.http.Part".equals(paramType.getName())) {
|
|
||||||
assertIsMultipartRequest(servletRequest);
|
|
||||||
arg = servletRequest.getPart(partName);
|
|
||||||
}
|
|
||||||
else if (isPartCollection(parameter)) {
|
|
||||||
assertIsMultipartRequest(servletRequest);
|
|
||||||
arg = new ArrayList<Object>(servletRequest.getParts());
|
|
||||||
}
|
|
||||||
else if (isPartArray(parameter)) {
|
|
||||||
assertIsMultipartRequest(servletRequest);
|
|
||||||
arg = RequestPartResolver.resolvePart(servletRequest);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, partName);
|
HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, name);
|
||||||
arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());
|
arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());
|
||||||
WebDataBinder binder = binderFactory.createBinder(request, arg, partName);
|
WebDataBinder binder = binderFactory.createBinder(request, arg, name);
|
||||||
if (arg != null) {
|
if (arg != null) {
|
||||||
validateIfApplicable(binder, parameter);
|
validateIfApplicable(binder, parameter);
|
||||||
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
|
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
|
||||||
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
|
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + partName, binder.getBindingResult());
|
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
|
||||||
}
|
}
|
||||||
catch (MissingServletRequestPartException ex) {
|
catch (MissingServletRequestPartException ex) {
|
||||||
// handled below
|
if (isRequired) {
|
||||||
arg = null;
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MultipartException ex) {
|
||||||
|
if (isRequired) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestPart requestPart = parameter.getParameterAnnotation(RequestPart.class);
|
|
||||||
boolean isRequired = ((requestPart == null || requestPart.required()) && !optional);
|
|
||||||
|
|
||||||
if (arg == null && isRequired) {
|
if (arg == null && isRequired) {
|
||||||
throw new MissingServletRequestPartException(partName);
|
if (!MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
|
||||||
|
throw new MultipartException("Current request is not a multipart request");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new MissingServletRequestPartException(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (optional) {
|
if (parameter.isOptional()) {
|
||||||
arg = Optional.ofNullable(arg);
|
arg = OptionalResolver.resolveValue(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertIsMultipartRequest(HttpServletRequest request) {
|
private String getPartName(MethodParameter methodParam, RequestPart requestPart) {
|
||||||
String contentType = request.getContentType();
|
|
||||||
if (contentType == null || !contentType.toLowerCase().startsWith("multipart/")) {
|
|
||||||
throw new MultipartException("The current request is not a multipart request");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPartName(MethodParameter methodParam) {
|
|
||||||
RequestPart requestPart = methodParam.getParameterAnnotation(RequestPart.class);
|
|
||||||
String partName = (requestPart != null ? requestPart.name() : "");
|
String partName = (requestPart != null ? requestPart.name() : "");
|
||||||
if (partName.length() == 0) {
|
if (partName.length() == 0) {
|
||||||
partName = methodParam.getParameterName();
|
partName = methodParam.getParameterName();
|
||||||
|
|
@ -221,46 +178,18 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
|
||||||
return partName;
|
return partName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMultipartFileCollection(MethodParameter methodParam) {
|
|
||||||
Class<?> collectionType = getCollectionParameterType(methodParam);
|
|
||||||
return MultipartFile.class == collectionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMultipartFileArray(MethodParameter methodParam) {
|
|
||||||
Class<?> paramType = methodParam.getNestedParameterType().getComponentType();
|
|
||||||
return MultipartFile.class == paramType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPartCollection(MethodParameter methodParam) {
|
|
||||||
Class<?> collectionType = getCollectionParameterType(methodParam);
|
|
||||||
return (collectionType != null && "javax.servlet.http.Part".equals(collectionType.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPartArray(MethodParameter methodParam) {
|
|
||||||
Class<?> paramType = methodParam.getNestedParameterType().getComponentType();
|
|
||||||
return (paramType != null && "javax.servlet.http.Part".equals(paramType.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Class<?> getCollectionParameterType(MethodParameter methodParam) {
|
|
||||||
Class<?> paramType = methodParam.getNestedParameterType();
|
|
||||||
if (Collection.class == paramType || List.class.isAssignableFrom(paramType)){
|
|
||||||
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
|
|
||||||
if (valueType != null) {
|
|
||||||
return valueType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inner class to avoid hard-coded dependency on Servlet 3.0 Part type...
|
* Inner class to avoid hard-coded dependency on Java 8 Optional type...
|
||||||
*/
|
*/
|
||||||
private static class RequestPartResolver {
|
private static class OptionalResolver {
|
||||||
|
|
||||||
public static Object resolvePart(HttpServletRequest servletRequest) throws Exception {
|
public static Object resolveValue(Object value) {
|
||||||
Collection<Part> parts = servletRequest.getParts();
|
if (value == null || (value instanceof Collection && ((Collection) value).isEmpty()) ||
|
||||||
return parts.toArray(new Part[parts.size()]);
|
(value instanceof Object[] && ((Object[]) value).length == 0)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2014 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -52,7 +52,7 @@ public class ServletCookieValueMethodArgumentResolver extends AbstractCookieValu
|
||||||
protected Object resolveName(String cookieName, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
|
protected Object resolveName(String cookieName, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
|
||||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||||
Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);
|
Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);
|
||||||
if (Cookie.class.isAssignableFrom(parameter.getParameterType())) {
|
if (Cookie.class.isAssignableFrom(parameter.getNestedParameterType())) {
|
||||||
return cookieValue;
|
return cookieValue;
|
||||||
}
|
}
|
||||||
else if (cookieValue != null) {
|
else if (cookieValue != null) {
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,12 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
|
||||||
this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(0);
|
this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConcurrentResultMethodParameter(ConcurrentResultMethodParameter original) {
|
||||||
|
super(original);
|
||||||
|
this.returnValue = original.returnValue;
|
||||||
|
this.returnType = original.returnType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?> getParameterType() {
|
public Class<?> getParameterType() {
|
||||||
if (this.returnValue != null) {
|
if (this.returnValue != null) {
|
||||||
|
|
@ -283,6 +289,11 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
|
||||||
public Type getGenericParameterType() {
|
public Type getGenericParameterType() {
|
||||||
return this.returnType.getType();
|
return this.returnType.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConcurrentResultMethodParameter clone() {
|
||||||
|
return new ConcurrentResultMethodParameter(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -41,6 +41,7 @@ import org.springframework.mock.web.test.MockHttpServletResponse;
|
||||||
import org.springframework.mock.web.test.MockMultipartFile;
|
import org.springframework.mock.web.test.MockMultipartFile;
|
||||||
import org.springframework.mock.web.test.MockMultipartHttpServletRequest;
|
import org.springframework.mock.web.test.MockMultipartHttpServletRequest;
|
||||||
import org.springframework.mock.web.test.MockPart;
|
import org.springframework.mock.web.test.MockPart;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
|
@ -53,7 +54,6 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
import org.springframework.web.multipart.MultipartException;
|
import org.springframework.web.multipart.MultipartException;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||||
import org.springframework.web.multipart.support.RequestPartServletServerHttpRequest;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static org.mockito.BDDMockito.*;
|
import static org.mockito.BDDMockito.*;
|
||||||
|
|
@ -86,7 +86,9 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
private MethodParameter paramPartArray;
|
private MethodParameter paramPartArray;
|
||||||
private MethodParameter paramRequestParamAnnot;
|
private MethodParameter paramRequestParamAnnot;
|
||||||
private MethodParameter optionalMultipartFile;
|
private MethodParameter optionalMultipartFile;
|
||||||
|
private MethodParameter optionalMultipartFileList;
|
||||||
private MethodParameter optionalPart;
|
private MethodParameter optionalPart;
|
||||||
|
private MethodParameter optionalPartList;
|
||||||
private MethodParameter optionalRequestPart;
|
private MethodParameter optionalRequestPart;
|
||||||
|
|
||||||
private NativeWebRequest webRequest;
|
private NativeWebRequest webRequest;
|
||||||
|
|
@ -97,10 +99,7 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
Method method = getClass().getMethod("handle", SimpleBean.class, SimpleBean.class,
|
Method method = ReflectionUtils.findMethod(getClass(), "handle", (Class<?>[]) null);
|
||||||
SimpleBean.class, MultipartFile.class, List.class, MultipartFile[].class,
|
|
||||||
Integer.TYPE, MultipartFile.class, Part.class, List.class, Part[].class,
|
|
||||||
MultipartFile.class, Optional.class, Optional.class, Optional.class);
|
|
||||||
|
|
||||||
paramRequestPart = new SynthesizingMethodParameter(method, 0);
|
paramRequestPart = new SynthesizingMethodParameter(method, 0);
|
||||||
paramRequestPart.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
paramRequestPart.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||||
|
|
@ -119,9 +118,13 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
paramRequestParamAnnot = new SynthesizingMethodParameter(method, 11);
|
paramRequestParamAnnot = new SynthesizingMethodParameter(method, 11);
|
||||||
optionalMultipartFile = new SynthesizingMethodParameter(method, 12);
|
optionalMultipartFile = new SynthesizingMethodParameter(method, 12);
|
||||||
optionalMultipartFile.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
optionalMultipartFile.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||||
optionalPart = new SynthesizingMethodParameter(method, 13);
|
optionalMultipartFileList = new SynthesizingMethodParameter(method, 13);
|
||||||
|
optionalMultipartFileList.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||||
|
optionalPart = new SynthesizingMethodParameter(method, 14);
|
||||||
optionalPart.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
optionalPart.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||||
optionalRequestPart = new SynthesizingMethodParameter(method, 14);
|
optionalPartList = new SynthesizingMethodParameter(method, 15);
|
||||||
|
optionalPartList.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
|
||||||
|
optionalRequestPart = new SynthesizingMethodParameter(method, 16);
|
||||||
|
|
||||||
messageConverter = mock(HttpMessageConverter.class);
|
messageConverter = mock(HttpMessageConverter.class);
|
||||||
given(messageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
given(messageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||||
|
|
@ -136,35 +139,41 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
multipartRequest = new MockMultipartHttpServletRequest();
|
multipartRequest = new MockMultipartHttpServletRequest();
|
||||||
multipartRequest.addFile(multipartFile1);
|
multipartRequest.addFile(multipartFile1);
|
||||||
multipartRequest.addFile(multipartFile2);
|
multipartRequest.addFile(multipartFile2);
|
||||||
|
multipartRequest.addFile(new MockMultipartFile("otherPart", "", "text/plain", content));
|
||||||
webRequest = new ServletWebRequest(multipartRequest, new MockHttpServletResponse());
|
webRequest = new ServletWebRequest(multipartRequest, new MockHttpServletResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void supportsParameter() {
|
public void supportsParameter() {
|
||||||
assertTrue("RequestPart parameter not supported", resolver.supportsParameter(paramRequestPart));
|
assertTrue(resolver.supportsParameter(paramRequestPart));
|
||||||
assertTrue("MultipartFile parameter not supported", resolver.supportsParameter(paramMultipartFileNotAnnot));
|
assertTrue(resolver.supportsParameter(paramNamedRequestPart));
|
||||||
assertTrue("Part parameter not supported", resolver.supportsParameter(paramPart));
|
assertTrue(resolver.supportsParameter(paramValidRequestPart));
|
||||||
assertTrue("List<Part> parameter not supported", resolver.supportsParameter(paramPartList));
|
assertTrue(resolver.supportsParameter(paramMultipartFile));
|
||||||
assertTrue("Part[] parameter not supported", resolver.supportsParameter(paramPartArray));
|
assertTrue(resolver.supportsParameter(paramMultipartFileList));
|
||||||
assertTrue("MultipartFile parameter not supported", resolver.supportsParameter(paramMultipartFile));
|
assertTrue(resolver.supportsParameter(paramMultipartFileArray));
|
||||||
assertTrue("List<MultipartFile> parameter not supported", resolver.supportsParameter(paramMultipartFileList));
|
assertFalse(resolver.supportsParameter(paramInt));
|
||||||
assertTrue("MultipartFile[] parameter not supported", resolver.supportsParameter(paramMultipartFileArray));
|
assertTrue(resolver.supportsParameter(paramMultipartFileNotAnnot));
|
||||||
assertFalse("non-RequestPart parameter should not be supported", resolver.supportsParameter(paramInt));
|
assertTrue(resolver.supportsParameter(paramPart));
|
||||||
assertFalse("@RequestParam args should not be supported", resolver.supportsParameter(paramRequestParamAnnot));
|
assertTrue(resolver.supportsParameter(paramPartList));
|
||||||
|
assertTrue(resolver.supportsParameter(paramPartArray));
|
||||||
|
assertFalse(resolver.supportsParameter(paramRequestParamAnnot));
|
||||||
|
assertTrue(resolver.supportsParameter(optionalMultipartFile));
|
||||||
|
assertTrue(resolver.supportsParameter(optionalMultipartFileList));
|
||||||
|
assertTrue(resolver.supportsParameter(optionalPart));
|
||||||
|
assertTrue(resolver.supportsParameter(optionalPartList));
|
||||||
|
assertTrue(resolver.supportsParameter(optionalRequestPart));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveMultipartFile() throws Exception {
|
public void resolveMultipartFile() throws Exception {
|
||||||
Object actual = resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
|
Object actual = resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
|
||||||
assertNotNull(actual);
|
|
||||||
assertSame(multipartFile1, actual);
|
assertSame(multipartFile1, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveMultipartFileList() throws Exception {
|
public void resolveMultipartFileList() throws Exception {
|
||||||
Object actual = resolver.resolveArgument(paramMultipartFileList, null, webRequest, null);
|
Object actual = resolver.resolveArgument(paramMultipartFileList, null, webRequest, null);
|
||||||
assertNotNull(actual);
|
|
||||||
assertTrue(actual instanceof List);
|
assertTrue(actual instanceof List);
|
||||||
assertEquals(Arrays.asList(multipartFile1, multipartFile2), actual);
|
assertEquals(Arrays.asList(multipartFile1, multipartFile2), actual);
|
||||||
}
|
}
|
||||||
|
|
@ -175,6 +184,7 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
assertNotNull(actual);
|
assertNotNull(actual);
|
||||||
assertTrue(actual instanceof MultipartFile[]);
|
assertTrue(actual instanceof MultipartFile[]);
|
||||||
MultipartFile[] parts = (MultipartFile[]) actual;
|
MultipartFile[] parts = (MultipartFile[]) actual;
|
||||||
|
assertEquals(2, parts.length);
|
||||||
assertEquals(parts[0], multipartFile1);
|
assertEquals(parts[0], multipartFile1);
|
||||||
assertEquals(parts[1], multipartFile2);
|
assertEquals(parts[1], multipartFile2);
|
||||||
}
|
}
|
||||||
|
|
@ -184,6 +194,7 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
||||||
MultipartFile expected = new MockMultipartFile("multipartFileNotAnnot", "Hello World".getBytes());
|
MultipartFile expected = new MockMultipartFile("multipartFileNotAnnot", "Hello World".getBytes());
|
||||||
request.addFile(expected);
|
request.addFile(expected);
|
||||||
|
request.addFile(new MockMultipartFile("otherPart", "", "text/plain", "Hello World".getBytes()));
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramMultipartFileNotAnnot, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramMultipartFileNotAnnot, null, webRequest, null);
|
||||||
|
|
@ -194,52 +205,52 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolvePartArgument() throws Exception {
|
public void resolvePartArgument() throws Exception {
|
||||||
MockPart expected = new MockPart("part", "Hello World".getBytes());
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setMethod("POST");
|
request.setMethod("POST");
|
||||||
request.setContentType("multipart/form-data");
|
request.setContentType("multipart/form-data");
|
||||||
|
MockPart expected = new MockPart("part", "Hello World".getBytes());
|
||||||
request.addPart(expected);
|
request.addPart(expected);
|
||||||
|
request.addPart(new MockPart("otherPart", "Hello World".getBytes()));
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramPart, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramPart, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof Part);
|
assertTrue(result instanceof Part);
|
||||||
assertEquals("Invalid result", expected, result);
|
assertEquals("Invalid result", expected, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolvePartListArgument() throws Exception {
|
public void resolvePartListArgument() throws Exception {
|
||||||
MockPart part1 = new MockPart("requestPart1", "Hello World 1".getBytes());
|
|
||||||
MockPart part2 = new MockPart("requestPart2", "Hello World 2".getBytes());
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setMethod("POST");
|
request.setMethod("POST");
|
||||||
request.setContentType("multipart/form-data");
|
request.setContentType("multipart/form-data");
|
||||||
|
MockPart part1 = new MockPart("requestPart", "Hello World 1".getBytes());
|
||||||
|
MockPart part2 = new MockPart("requestPart", "Hello World 2".getBytes());
|
||||||
request.addPart(part1);
|
request.addPart(part1);
|
||||||
request.addPart(part2);
|
request.addPart(part2);
|
||||||
|
request.addPart(new MockPart("otherPart", "Hello World".getBytes()));
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramPartList, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramPartList, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof List);
|
assertTrue(result instanceof List);
|
||||||
assertEquals(Arrays.asList(part1, part2), result);
|
assertEquals(Arrays.asList(part1, part2), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolvePartArrayArgument() throws Exception {
|
public void resolvePartArrayArgument() throws Exception {
|
||||||
MockPart part1 = new MockPart("requestPart1", "Hello World 1".getBytes());
|
|
||||||
MockPart part2 = new MockPart("requestPart2", "Hello World 2".getBytes());
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setMethod("POST");
|
request.setMethod("POST");
|
||||||
request.setContentType("multipart/form-data");
|
request.setContentType("multipart/form-data");
|
||||||
|
MockPart part1 = new MockPart("requestPart", "Hello World 1".getBytes());
|
||||||
|
MockPart part2 = new MockPart("requestPart", "Hello World 2".getBytes());
|
||||||
request.addPart(part1);
|
request.addPart(part1);
|
||||||
request.addPart(part2);
|
request.addPart(part2);
|
||||||
|
request.addPart(new MockPart("otherPart", "Hello World".getBytes()));
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(paramPartArray, null, webRequest, null);
|
Object result = resolver.resolveArgument(paramPartArray, null, webRequest, null);
|
||||||
|
|
||||||
assertTrue(result instanceof Part[]);
|
assertTrue(result instanceof Part[]);
|
||||||
Part[] parts = (Part[]) result;
|
Part[] parts = (Part[]) result;
|
||||||
assertThat(parts, Matchers.arrayWithSize(2));
|
assertEquals(2, parts.length);
|
||||||
assertEquals(parts[0], part1);
|
assertEquals(parts[0], part1);
|
||||||
assertEquals(parts[1], part2);
|
assertEquals(parts[1], part2);
|
||||||
}
|
}
|
||||||
|
|
@ -302,8 +313,8 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
@Test // SPR-9079
|
@Test // SPR-9079
|
||||||
public void isMultipartRequestPut() throws Exception {
|
public void isMultipartRequestPut() throws Exception {
|
||||||
this.multipartRequest.setMethod("PUT");
|
this.multipartRequest.setMethod("PUT");
|
||||||
Object actual = resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
|
Object actualValue = resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
|
||||||
assertSame(multipartFile1, actual);
|
assertSame(multipartFile1, actualValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -311,12 +322,16 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
||||||
MultipartFile expected = new MockMultipartFile("optionalMultipartFile", "Hello World".getBytes());
|
MultipartFile expected = new MockMultipartFile("optionalMultipartFile", "Hello World".getBytes());
|
||||||
request.addFile(expected);
|
request.addFile(expected);
|
||||||
|
request.addFile(new MockMultipartFile("otherPart", "", "text/plain", "Hello World".getBytes()));
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
|
Object actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
|
||||||
|
assertTrue(actualValue instanceof Optional);
|
||||||
|
assertEquals("Invalid result", expected, ((Optional) actualValue).get());
|
||||||
|
|
||||||
assertTrue(result instanceof Optional);
|
actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
|
||||||
assertEquals("Invalid result", expected, ((Optional) result).get());
|
assertTrue(actualValue instanceof Optional);
|
||||||
|
assertEquals("Invalid result", expected, ((Optional) actualValue).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -324,10 +339,62 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
|
Object actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
|
||||||
assertTrue(result instanceof Optional);
|
actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
|
||||||
assertFalse("Invalid result", ((Optional) result).isPresent());
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveOptionalMultipartFileArgumentWithoutMultipartRequest() throws Exception {
|
||||||
|
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveOptionalMultipartFileList() throws Exception {
|
||||||
|
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
||||||
|
MultipartFile expected = new MockMultipartFile("requestPart", "Hello World".getBytes());
|
||||||
|
request.addFile(expected);
|
||||||
|
request.addFile(new MockMultipartFile("otherPart", "", "text/plain", "Hello World".getBytes()));
|
||||||
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalMultipartFileList, null, webRequest, null);
|
||||||
|
assertTrue(actualValue instanceof Optional);
|
||||||
|
assertEquals("Invalid result", Collections.singletonList(expected), ((Optional) actualValue).get());
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalMultipartFileList, null, webRequest, null);
|
||||||
|
assertTrue(actualValue instanceof Optional);
|
||||||
|
assertEquals("Invalid result", Collections.singletonList(expected), ((Optional) actualValue).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveOptionalMultipartFileListNotPresent() throws Exception {
|
||||||
|
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
||||||
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalMultipartFileList, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalMultipartFileList, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveOptionalMultipartFileListWithoutMultipartRequest() throws Exception {
|
||||||
|
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalMultipartFileList, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalMultipartFileList, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -337,12 +404,16 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
request.setMethod("POST");
|
request.setMethod("POST");
|
||||||
request.setContentType("multipart/form-data");
|
request.setContentType("multipart/form-data");
|
||||||
request.addPart(expected);
|
request.addPart(expected);
|
||||||
|
request.addPart(new MockPart("otherPart", "Hello World".getBytes()));
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(optionalPart, null, webRequest, null);
|
Object actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null);
|
||||||
|
assertTrue(actualValue instanceof Optional);
|
||||||
|
assertEquals("Invalid result", expected, ((Optional) actualValue).get());
|
||||||
|
|
||||||
assertTrue(result instanceof Optional);
|
actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null);
|
||||||
assertEquals("Invalid result", expected, ((Optional) result).get());
|
assertTrue(actualValue instanceof Optional);
|
||||||
|
assertEquals("Invalid result", expected, ((Optional) actualValue).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -352,36 +423,108 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
request.setContentType("multipart/form-data");
|
request.setContentType("multipart/form-data");
|
||||||
webRequest = new ServletWebRequest(request);
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
Object result = resolver.resolveArgument(optionalPart, null, webRequest, null);
|
Object actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
|
||||||
assertTrue(result instanceof Optional);
|
actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null);
|
||||||
assertFalse("Invalid result", ((Optional) result).isPresent());
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveOptionalPartArgumentWithoutMultipartRequest() throws Exception {
|
||||||
|
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveOptionalPartList() throws Exception {
|
||||||
|
MockPart expected = new MockPart("requestPart", "Hello World".getBytes());
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.setMethod("POST");
|
||||||
|
request.setContentType("multipart/form-data");
|
||||||
|
request.addPart(expected);
|
||||||
|
request.addPart(new MockPart("otherPart", "Hello World".getBytes()));
|
||||||
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalPartList, null, webRequest, null);
|
||||||
|
assertTrue(actualValue instanceof Optional);
|
||||||
|
assertEquals("Invalid result", Collections.singletonList(expected), ((Optional) actualValue).get());
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalPartList, null, webRequest, null);
|
||||||
|
assertTrue(actualValue instanceof Optional);
|
||||||
|
assertEquals("Invalid result", Collections.singletonList(expected), ((Optional) actualValue).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveOptionalPartListNotPresent() throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.setMethod("POST");
|
||||||
|
request.setContentType("multipart/form-data");
|
||||||
|
webRequest = new ServletWebRequest(request);
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalPartList, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalPartList, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveOptionalPartListWithoutMultipartRequest() throws Exception {
|
||||||
|
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalPartList, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalPartList, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveOptionalRequestPart() throws Exception {
|
public void resolveOptionalRequestPart() throws Exception {
|
||||||
SimpleBean simpleBean = new SimpleBean("foo");
|
SimpleBean simpleBean = new SimpleBean("foo");
|
||||||
|
|
||||||
given(messageConverter.canRead(SimpleBean.class, MediaType.TEXT_PLAIN)).willReturn(true);
|
given(messageConverter.canRead(SimpleBean.class, MediaType.TEXT_PLAIN)).willReturn(true);
|
||||||
given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(simpleBean);
|
given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(simpleBean);
|
||||||
|
|
||||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||||
Object actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory());
|
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||||
|
assertEquals("Invalid argument value", Optional.of(simpleBean), actualValue);
|
||||||
|
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||||
assertEquals("Invalid argument value", Optional.of(simpleBean), actualValue);
|
assertEquals("Invalid argument value", Optional.of(simpleBean), actualValue);
|
||||||
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveOptionalRequestPartNotPresent() throws Exception {
|
public void resolveOptionalRequestPartNotPresent() throws Exception {
|
||||||
given(messageConverter.canRead(SimpleBean.class, MediaType.TEXT_PLAIN)).willReturn(true);
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
given(messageConverter.read(eq(SimpleBean.class), isA(RequestPartServletServerHttpRequest.class))).willReturn(null);
|
request.setMethod("POST");
|
||||||
|
request.setContentType("multipart/form-data");
|
||||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
webRequest = new ServletWebRequest(request);
|
||||||
Object actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory());
|
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalRequestPart, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalRequestPart, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveOptionalRequestPartWithoutMultipartRequest() throws Exception {
|
||||||
|
webRequest = new ServletWebRequest(new MockHttpServletRequest());
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(optionalRequestPart, null, webRequest, null);
|
||||||
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
|
|
||||||
|
actualValue = resolver.resolveArgument(optionalRequestPart, null, webRequest, null);
|
||||||
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
assertEquals("Invalid argument value", Optional.empty(), actualValue);
|
||||||
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -390,8 +533,8 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(argValue);
|
given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(argValue);
|
||||||
|
|
||||||
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
|
||||||
Object actualValue = resolver.resolveArgument(parameter, mavContainer, webRequest, new ValidatingBinderFactory());
|
|
||||||
|
|
||||||
|
Object actualValue = resolver.resolveArgument(parameter, mavContainer, webRequest, new ValidatingBinderFactory());
|
||||||
assertEquals("Invalid argument value", argValue, actualValue);
|
assertEquals("Invalid argument value", argValue, actualValue);
|
||||||
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
|
||||||
}
|
}
|
||||||
|
|
@ -425,22 +568,26 @@ public class RequestPartMethodArgumentResolverTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public void handle(@RequestPart SimpleBean requestPart,
|
public void handle(
|
||||||
@RequestPart(value="requestPart", required=false) SimpleBean namedRequestPart,
|
@RequestPart SimpleBean requestPart,
|
||||||
@Valid @RequestPart("requestPart") SimpleBean validRequestPart,
|
@RequestPart(value="requestPart", required=false) SimpleBean namedRequestPart,
|
||||||
@RequestPart("requestPart") MultipartFile multipartFile,
|
@Valid @RequestPart("requestPart") SimpleBean validRequestPart,
|
||||||
@RequestPart("requestPart") List<MultipartFile> multipartFileList,
|
@RequestPart("requestPart") MultipartFile multipartFile,
|
||||||
@RequestPart("requestPart") MultipartFile[] multipartFileArray,
|
@RequestPart("requestPart") List<MultipartFile> multipartFileList,
|
||||||
int i,
|
@RequestPart("requestPart") MultipartFile[] multipartFileArray,
|
||||||
MultipartFile multipartFileNotAnnot,
|
int i,
|
||||||
Part part,
|
MultipartFile multipartFileNotAnnot,
|
||||||
@RequestPart("part") List<Part> partList,
|
Part part,
|
||||||
@RequestPart("part") Part[] partArray,
|
@RequestPart("requestPart") List<Part> partList,
|
||||||
@RequestParam MultipartFile requestParamAnnot,
|
@RequestPart("requestPart") Part[] partArray,
|
||||||
Optional<MultipartFile> optionalMultipartFile,
|
@RequestParam MultipartFile requestParamAnnot,
|
||||||
Optional<Part> optionalPart,
|
Optional<MultipartFile> optionalMultipartFile,
|
||||||
@RequestPart("requestPart") Optional<SimpleBean> optionalRequestPart) {
|
@RequestPart("requestPart") Optional<List<MultipartFile>> optionalMultipartFileList,
|
||||||
|
Optional<Part> optionalPart,
|
||||||
|
@RequestPart("requestPart") Optional<List<Part>> optionalPartList,
|
||||||
|
@RequestPart("requestPart") Optional<SimpleBean> optionalRequestPart) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -47,7 +47,9 @@ import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMeth
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/** @author Arjen Poutsma */
|
/**
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
*/
|
||||||
public class DefaultHandlerExceptionResolverTests {
|
public class DefaultHandlerExceptionResolverTests {
|
||||||
|
|
||||||
private DefaultHandlerExceptionResolver exceptionResolver;
|
private DefaultHandlerExceptionResolver exceptionResolver;
|
||||||
|
|
@ -174,7 +176,9 @@ public class DefaultHandlerExceptionResolverTests {
|
||||||
assertNotNull("No ModelAndView returned", mav);
|
assertNotNull("No ModelAndView returned", mav);
|
||||||
assertTrue("No Empty ModelAndView returned", mav.isEmpty());
|
assertTrue("No Empty ModelAndView returned", mav.isEmpty());
|
||||||
assertEquals("Invalid status code", 400, response.getStatus());
|
assertEquals("Invalid status code", 400, response.getStatus());
|
||||||
assertEquals("Required request part 'name' is not present.", response.getErrorMessage());
|
assertTrue(response.getErrorMessage().contains("request part"));
|
||||||
|
assertTrue(response.getErrorMessage().contains("name"));
|
||||||
|
assertTrue(response.getErrorMessage().contains("not present"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -211,6 +215,7 @@ public class DefaultHandlerExceptionResolverTests {
|
||||||
assertSame(ex, request.getAttribute("javax.servlet.error.exception"));
|
assertSame(ex, request.getAttribute("javax.servlet.error.exception"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public void handle(String arg) {
|
public void handle(String arg) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue