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");
* 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 Sam Brannen
* @since 3.1
*
* @see RequestParam
* @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");
* you may not use this file except in compliance with the License.
@@ -280,6 +280,10 @@ public class HandlerMethod {
super(HandlerMethod.this.bridgedMethod, index);
}
protected HandlerMethodParameter(HandlerMethodParameter original) {
super(original);
}
@Override
public Class<?> getContainingClass() {
return HandlerMethod.this.getBeanType();
@@ -289,6 +293,11 @@ public class HandlerMethod {
public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
return HandlerMethod.this.getMethodAnnotation(annotationType);
}
@Override
public HandlerMethodParameter clone() {
return new HandlerMethodParameter(this);
}
}
@@ -304,10 +313,20 @@ public class HandlerMethod {
this.returnValue = returnValue;
}
protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
super(original);
this.returnValue = original.returnValue;
}
@Override
public Class<?> 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");
* you may not use this file except in compliance with the License.
@@ -63,7 +63,7 @@ public abstract class AbstractCookieValueMethodArgumentResolver extends Abstract
@Override
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
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");
* 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.ConfigurableBeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ValueConstants;
import org.springframework.web.bind.support.WebDataBinderFactory;
@@ -53,6 +54,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.1
*/
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
@@ -61,7 +63,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
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() {
@@ -84,18 +86,18 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
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 (namedValueInfo.defaultValue != null) {
arg = resolveDefaultValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !parameter.getParameterType().getName().equals("java.util.Optional")) {
handleMissingValue(namedValueInfo.name, parameter);
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
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) {
arg = resolveDefaultValue(namedValueInfo.defaultValue);
@@ -104,7 +106,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, paramType, parameter);
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
@@ -151,7 +153,8 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
if (info.name.length() == 0) {
name = parameter.getParameterName();
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.");
}
}
@@ -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 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
* @return the resolved argument. May be {@code null}
* @return the resolved argument (may be {@code null})
* @throws Exception in case of errors
*/
protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request)
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) {
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.
* @param name the name for the value
* @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.
@@ -202,7 +221,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
return Boolean.FALSE;
}
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 " +
"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
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(RequestHeader.class) &&
!Map.class.isAssignableFrom(parameter.getParameterType()));
!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType()));
}
@Override
@@ -79,7 +79,7 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
@Override
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
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");
* you may not use this file except in compliance with the License.
@@ -17,22 +17,17 @@
package org.springframework.web.method.annotation;
import java.beans.PropertyEditor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.MissingServletRequestParameterException;
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.MultipartHttpServletRequest;
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.WebUtils;
@@ -124,9 +121,8 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(paramType)) {
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
String paramName = parameter.getParameterAnnotation(RequestParam.class).name();
return StringUtils.hasText(paramName);
}
@@ -138,11 +134,12 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
else if (MultipartFile.class == paramType || "javax.servlet.http.Part".equals(paramType.getName())) {
parameter = parameter.nestedIfOptional();
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(paramType);
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
return false;
@@ -157,112 +154,61 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
}
@Override
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
MultipartHttpServletRequest multipartRequest =
WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
Object arg;
if (MultipartFile.class == parameter.getParameterType()) {
assertIsMultipartRequest(servletRequest);
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
arg = multipartRequest.getFile(name);
}
else if (isMultipartFileCollection(parameter)) {
assertIsMultipartRequest(servletRequest);
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
arg = multipartRequest.getFiles(name);
}
else if (isMultipartFileArray(parameter)) {
assertIsMultipartRequest(servletRequest);
Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
List<MultipartFile> multipartFiles = multipartRequest.getFiles(name);
arg = multipartFiles.toArray(new MultipartFile[multipartFiles.size()]);
}
else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {
assertIsMultipartRequest(servletRequest);
arg = servletRequest.getPart(name);
}
else if (isPartCollection(parameter)) {
assertIsMultipartRequest(servletRequest);
arg = new ArrayList<Object>(servletRequest.getParts());
}
else if (isPartArray(parameter)) {
assertIsMultipartRequest(servletRequest);
arg = RequestPartResolver.resolvePart(servletRequest);
}
else {
arg = null;
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
if (arg == null) {
String[] paramValues = webRequest.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
Object arg = null;
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
if (arg == null) {
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
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;
@Override
protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
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);
}
}
return null;
}
@Override
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
throw new MissingServletRequestParameterException(name, parameter.getParameterType().getSimpleName());
else {
throw new MissingServletRequestParameterException(name, parameter.getNestedParameterType().getSimpleName());
}
}
@Override
public void contributeMethodArgument(MethodParameter parameter, Object value,
UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) {
Class<?> paramType = parameter.getParameterType();
Class<?> paramType = parameter.getNestedParameterType();
if (Map.class.isAssignableFrom(paramType) || MultipartFile.class == paramType ||
"javax.servlet.http.Part".equals(paramType.getName())) {
return;
}
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) {
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");
* 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
* name cannot be found.
*
* <p>This may be because the request is not a multipart/form-data
*
* either because the part is not present in the request, or
* because the web application is not configured correctly for processing
* multipart requests -- e.g. no {@link MultipartResolver}.
* <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
* application is not configured correctly for processing multipart requests,
* e.g. no {@link MultipartResolver}.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
@SuppressWarnings("serial")
public class MissingServletRequestPartException extends ServletException {
private static final long serialVersionUID = -1255077391966870705L;
private final 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;
}
public String getRequestPartName() {
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");
* 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.server.ServerHttpRequest;
import org.springframework.http.server.ServletServerHttpRequest;
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.multipart.MultipartResolver;
import org.springframework.web.util.WebUtils;
/**
* {@link ServerHttpRequest} implementation that accesses one part of a multipart
@@ -54,46 +51,26 @@ public class RequestPartServletServerHttpRequest extends ServletServerHttpReques
/**
* Create a new instance.
* @param request the current request
* Create a new {@code RequestPartServletServerHttpRequest} instance.
* @param request the current servlet request
* @param partName the name of the part to adapt to the {@link ServerHttpRequest} contract
* @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)
throws MissingServletRequestPartException {
super(request);
this.multipartRequest = asMultipartRequest(request);
this.multipartRequest = MultipartResolutionDelegate.asMultipartHttpServletRequest(request);
this.partName = partName;
this.headers = this.multipartRequest.getMultipartHeaders(this.partName);
if (this.headers == null) {
if (request instanceof MultipartHttpServletRequest) {
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.");
}
throw new MissingServletRequestPartException(partName);
}
}
private static MultipartHttpServletRequest asMultipartRequest(HttpServletRequest request) {
MultipartHttpServletRequest unwrapped = WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
if (unwrapped != null) {
return unwrapped;
}
else if (ClassUtils.hasMethod(HttpServletRequest.class, "getParts")) {
// Servlet 3.0 available ..
return new StandardMultipartHttpServletRequest(request);
}
throw new IllegalArgumentException("Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
}
@Override
public HttpHeaders getHeaders() {