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:
Juergen Hoeller 2016-01-26 18:06:15 +01:00
parent a3a5a03ee3
commit b4f33adf48
23 changed files with 820 additions and 451 deletions

View File

@ -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.

View File

@ -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);
}
} }

View File

@ -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);
}
} }
} }

View File

@ -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));
} }

View File

@ -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);
} }
} }
} }

View File

@ -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);
}
} }
} }

View File

@ -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
*/ */

View File

@ -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);
}
} }
} }

View File

@ -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());
} }

View File

@ -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.");
} }

View File

@ -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());
} }

View File

@ -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,42 +154,17 @@ 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); Object arg = null;
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) { if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name); List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) { if (!files.isEmpty()) {
@ -200,69 +172,43 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
} }
} }
if (arg == null) { if (arg == null) {
String[] paramValues = webRequest.getParameterValues(name); String[] paramValues = request.getParameterValues(name);
if (paramValues != null) { if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues); arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
} }
} }
}
return arg; return arg;
} }
private void assertIsMultipartRequest(HttpServletRequest request) {
String contentType = request.getContentType();
if (contentType == null || !contentType.toLowerCase().startsWith("multipart/")) {
throw new MultipartException("The current request is not a multipart request");
}
}
private boolean isMultipartFileCollection(MethodParameter parameter) {
return (MultipartFile.class == getCollectionParameterType(parameter));
}
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;
}
@Override @Override
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException { protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
throw new MissingServletRequestParameterException(name, parameter.getParameterType().getSimpleName()); HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
if (!MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
throw new MultipartException("Current request is not a multipart request");
}
else {
throw new MissingServletRequestPartException(name);
}
}
else {
throw new MissingServletRequestParameterException(name, parameter.getNestedParameterType().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()]);
}
}
} }

View File

@ -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;
} }
} }

View File

@ -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()]);
}
}
}

View File

@ -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,44 +51,24 @@ 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?");
} }

View File

@ -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,7 +160,6 @@ 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);
} }
@ -165,7 +170,6 @@ public class RequestParamMethodArgumentResolverTests {
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) {
} }
} }

View File

@ -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
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, MultiValueMap<String, String>> pathParameters = protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
(Map<String, MultiValueMap<String, String>>) request.getAttribute( Map<String, MultiValueMap<String, String>> pathParameters = (Map<String, MultiValueMap<String, String>>)
HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); 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());
} }
} }
} }

View File

@ -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;
} }

View File

@ -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 {
@ -110,15 +104,7 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
if (parameter.hasParameterAnnotation(RequestParam.class)) { if (parameter.hasParameterAnnotation(RequestParam.class)) {
return false; return false;
} }
else if (MultipartFile.class == parameter.getParameterType()) { return MultipartResolutionDelegate.isMultipartArgument(parameter.nestedIfOptional());
return true;
}
else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {
return true;
}
else {
return false;
}
} }
} }
@ -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");
} }
if (optional) { else {
arg = Optional.ofNullable(arg); throw new MissingServletRequestPartException(name);
}
}
if (parameter.isOptional()) {
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);
} }
} }

View File

@ -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) {

View File

@ -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);
}
} }
} }

View File

@ -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,8 +568,10 @@ public class RequestPartMethodArgumentResolverTests {
} }
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void handle(@RequestPart SimpleBean requestPart, public void handle(
@RequestPart SimpleBean requestPart,
@RequestPart(value="requestPart", required=false) SimpleBean namedRequestPart, @RequestPart(value="requestPart", required=false) SimpleBean namedRequestPart,
@Valid @RequestPart("requestPart") SimpleBean validRequestPart, @Valid @RequestPart("requestPart") SimpleBean validRequestPart,
@RequestPart("requestPart") MultipartFile multipartFile, @RequestPart("requestPart") MultipartFile multipartFile,
@ -435,11 +580,13 @@ public class RequestPartMethodArgumentResolverTests {
int i, int i,
MultipartFile multipartFileNotAnnot, MultipartFile multipartFileNotAnnot,
Part part, Part part,
@RequestPart("part") List<Part> partList, @RequestPart("requestPart") List<Part> partList,
@RequestPart("part") Part[] partArray, @RequestPart("requestPart") Part[] partArray,
@RequestParam MultipartFile requestParamAnnot, @RequestParam MultipartFile requestParamAnnot,
Optional<MultipartFile> optionalMultipartFile, Optional<MultipartFile> optionalMultipartFile,
@RequestPart("requestPart") Optional<List<MultipartFile>> optionalMultipartFileList,
Optional<Part> optionalPart, Optional<Part> optionalPart,
@RequestPart("requestPart") Optional<List<Part>> optionalPartList,
@RequestPart("requestPart") Optional<SimpleBean> optionalRequestPart) { @RequestPart("requestPart") Optional<SimpleBean> optionalRequestPart) {
} }

View File

@ -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) {
} }