Skip to content

Commit f0f1979

Browse files
committed
Support for @RequestParam Map declared with MultipartFile/Part values
Issue: SPR-17405
1 parent 488a1d4 commit f0f1979

File tree

4 files changed

+246
-95
lines changed

4 files changed

+246
-95
lines changed

spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java

+77-21
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616

1717
package org.springframework.web.method.annotation;
1818

19+
import java.util.Collection;
1920
import java.util.LinkedHashMap;
2021
import java.util.Map;
22+
import javax.servlet.http.HttpServletRequest;
23+
import javax.servlet.http.Part;
2124

2225
import org.springframework.core.MethodParameter;
26+
import org.springframework.core.ResolvableType;
2327
import org.springframework.lang.Nullable;
2428
import org.springframework.util.LinkedMultiValueMap;
2529
import org.springframework.util.MultiValueMap;
@@ -29,22 +33,29 @@
2933
import org.springframework.web.context.request.NativeWebRequest;
3034
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
3135
import org.springframework.web.method.support.ModelAndViewContainer;
36+
import org.springframework.web.multipart.MultipartFile;
37+
import org.springframework.web.multipart.MultipartRequest;
38+
import org.springframework.web.multipart.support.MultipartResolutionDelegate;
3239

3340
/**
3441
* Resolves {@link Map} method arguments annotated with an @{@link RequestParam}
3542
* where the annotation does not specify a request parameter name.
36-
* See {@link RequestParamMethodArgumentResolver} for resolving {@link Map}
37-
* method arguments with a request parameter name.
3843
*
39-
* <p>The created {@link Map} contains all request parameter name/value pairs.
40-
* If the method parameter type is {@link MultiValueMap} instead, the created
41-
* map contains all request parameters and all there values for cases where
42-
* request parameters have multiple values.
44+
* <p>The created {@link Map} contains all request parameter name/value pairs,
45+
* or all multipart files for a given parameter name if specifically declared
46+
* with {@link MultipartFile} as the value type. If the method parameter type is
47+
* {@link MultiValueMap} instead, the created map contains all request parameters
48+
* and all their values for cases where request parameters have multiple values
49+
* (or multiple multipart files of the same name).
4350
*
4451
* @author Arjen Poutsma
4552
* @author Rossen Stoyanchev
53+
* @author Juergen Hoeller
4654
* @since 3.1
4755
* @see RequestParamMethodArgumentResolver
56+
* @see HttpServletRequest#getParameterMap()
57+
* @see MultipartRequest#getMultiFileMap()
58+
* @see MultipartRequest#getFileMap()
4859
*/
4960
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
5061

@@ -59,26 +70,71 @@ public boolean supportsParameter(MethodParameter parameter) {
5970
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
6071
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
6172

62-
Class<?> paramType = parameter.getParameterType();
73+
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
6374

64-
Map<String, String[]> parameterMap = webRequest.getParameterMap();
65-
if (MultiValueMap.class.isAssignableFrom(paramType)) {
66-
MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());
67-
parameterMap.forEach((key, values) -> {
68-
for (String value : values) {
69-
result.add(key, value);
75+
if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
76+
// MultiValueMap
77+
Class<?> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve();
78+
if (valueType == MultipartFile.class) {
79+
MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
80+
return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0));
81+
}
82+
else if (valueType == Part.class) {
83+
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
84+
if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
85+
Collection<Part> parts = servletRequest.getParts();
86+
LinkedMultiValueMap<String, Part> result = new LinkedMultiValueMap<>(parts.size());
87+
for (Part part : parts) {
88+
result.add(part.getName(), part);
89+
}
90+
return result;
7091
}
71-
});
72-
return result;
92+
return new LinkedMultiValueMap<>(0);
93+
}
94+
else {
95+
Map<String, String[]> parameterMap = webRequest.getParameterMap();
96+
MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());
97+
parameterMap.forEach((key, values) -> {
98+
for (String value : values) {
99+
result.add(key, value);
100+
}
101+
});
102+
return result;
103+
}
73104
}
105+
74106
else {
75-
Map<String, String> result = new LinkedHashMap<>(parameterMap.size());
76-
parameterMap.forEach((key, values) -> {
77-
if (values.length > 0) {
78-
result.put(key, values[0]);
107+
// Regular Map
108+
Class<?> valueType = resolvableType.asMap().getGeneric(1).resolve();
109+
if (valueType == MultipartFile.class) {
110+
MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
111+
return (multipartRequest != null ? multipartRequest.getFileMap() : new LinkedHashMap<>(0));
112+
}
113+
else if (valueType == Part.class) {
114+
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
115+
if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
116+
Collection<Part> parts = servletRequest.getParts();
117+
LinkedHashMap<String, Part> result = new LinkedHashMap<>(parts.size());
118+
for (Part part : parts) {
119+
if (!result.containsKey(part.getName())) {
120+
result.put(part.getName(), part);
121+
}
122+
}
123+
return result;
79124
}
80-
});
81-
return result;
125+
return new LinkedHashMap<>(0);
126+
}
127+
else {
128+
Map<String, String[]> parameterMap = webRequest.getParameterMap();
129+
Map<String, String> result = new LinkedHashMap<>(parameterMap.size());
130+
parameterMap.forEach((key, values) -> {
131+
if (values.length > 0) {
132+
result.put(key, values[0]);
133+
}
134+
});
135+
return result;
136+
}
82137
}
83138
}
139+
84140
}

spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java

+19-19
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
import org.springframework.core.MethodParameter;
2626
import org.springframework.core.ResolvableType;
2727
import org.springframework.lang.Nullable;
28+
import org.springframework.web.context.request.NativeWebRequest;
2829
import org.springframework.web.multipart.MultipartFile;
2930
import org.springframework.web.multipart.MultipartHttpServletRequest;
31+
import org.springframework.web.multipart.MultipartRequest;
3032
import org.springframework.web.util.WebUtils;
3133

3234
/**
@@ -44,6 +46,19 @@ public abstract class MultipartResolutionDelegate {
4446
public static final Object UNRESOLVABLE = new Object();
4547

4648

49+
@Nullable
50+
public static MultipartRequest resolveMultipartRequest(NativeWebRequest webRequest) {
51+
MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
52+
if (multipartRequest != null) {
53+
return multipartRequest;
54+
}
55+
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
56+
if (servletRequest != null && isMultipartContent(servletRequest)) {
57+
return new StandardMultipartHttpServletRequest(servletRequest);
58+
}
59+
return null;
60+
}
61+
4762
public static boolean isMultipartRequest(HttpServletRequest request) {
4863
return (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null ||
4964
isMultipartContent(request));
@@ -103,13 +118,13 @@ else if (isMultipartFileArray(parameter)) {
103118
}
104119
}
105120
else if (Part.class == parameter.getNestedParameterType()) {
106-
return (isMultipart ? resolvePart(request, name) : null);
121+
return (isMultipart ? request.getPart(name): null);
107122
}
108123
else if (isPartCollection(parameter)) {
109124
return (isMultipart ? resolvePartList(request, name) : null);
110125
}
111126
else if (isPartArray(parameter)) {
112-
return (isMultipart ? resolvePartArray(request, name) : null);
127+
return (isMultipart ? resolvePartList(request, name).toArray(new Part[0]) : null);
113128
}
114129
else {
115130
return UNRESOLVABLE;
@@ -144,12 +159,8 @@ private static Class<?> getCollectionParameterType(MethodParameter methodParam)
144159
return null;
145160
}
146161

147-
private static Part resolvePart(HttpServletRequest servletRequest, String name) throws Exception {
148-
return servletRequest.getPart(name);
149-
}
150-
151-
private static List<Part> resolvePartList(HttpServletRequest servletRequest, String name) throws Exception {
152-
Collection<Part> parts = servletRequest.getParts();
162+
private static List<Part> resolvePartList(HttpServletRequest request, String name) throws Exception {
163+
Collection<Part> parts = request.getParts();
153164
List<Part> result = new ArrayList<>(parts.size());
154165
for (Part part : parts) {
155166
if (part.getName().equals(name)) {
@@ -159,15 +170,4 @@ private static List<Part> resolvePartList(HttpServletRequest servletRequest, Str
159170
return result;
160171
}
161172

162-
private static Part[] resolvePartArray(HttpServletRequest servletRequest, String name) throws Exception {
163-
Collection<Part> parts = servletRequest.getParts();
164-
List<Part> result = new ArrayList<>(parts.size());
165-
for (Part part : parts) {
166-
if (part.getName().equals(name)) {
167-
result.add(part);
168-
}
169-
}
170-
return result.toArray(new Part[0]);
171-
}
172-
173173
}

0 commit comments

Comments
 (0)