001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.method.annotation;
018
019import java.util.Collection;
020import java.util.LinkedHashMap;
021import java.util.Map;
022
023import javax.servlet.http.HttpServletRequest;
024import javax.servlet.http.Part;
025
026import org.springframework.core.MethodParameter;
027import org.springframework.core.ResolvableType;
028import org.springframework.lang.Nullable;
029import org.springframework.util.LinkedMultiValueMap;
030import org.springframework.util.MultiValueMap;
031import org.springframework.util.StringUtils;
032import org.springframework.web.bind.annotation.RequestParam;
033import org.springframework.web.bind.support.WebDataBinderFactory;
034import org.springframework.web.context.request.NativeWebRequest;
035import org.springframework.web.method.support.HandlerMethodArgumentResolver;
036import org.springframework.web.method.support.ModelAndViewContainer;
037import org.springframework.web.multipart.MultipartFile;
038import org.springframework.web.multipart.MultipartRequest;
039import org.springframework.web.multipart.support.MultipartResolutionDelegate;
040
041/**
042 * Resolves {@link Map} method arguments annotated with an @{@link RequestParam}
043 * where the annotation does not specify a request parameter name.
044 *
045 * <p>The created {@link Map} contains all request parameter name/value pairs,
046 * or all multipart files for a given parameter name if specifically declared
047 * with {@link MultipartFile} as the value type. If the method parameter type is
048 * {@link MultiValueMap} instead, the created map contains all request parameters
049 * and all their values for cases where request parameters have multiple values
050 * (or multiple multipart files of the same name).
051 *
052 * @author Arjen Poutsma
053 * @author Rossen Stoyanchev
054 * @author Juergen Hoeller
055 * @since 3.1
056 * @see RequestParamMethodArgumentResolver
057 * @see HttpServletRequest#getParameterMap()
058 * @see MultipartRequest#getMultiFileMap()
059 * @see MultipartRequest#getFileMap()
060 */
061public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
062
063        @Override
064        public boolean supportsParameter(MethodParameter parameter) {
065                RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
066                return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
067                                !StringUtils.hasText(requestParam.name()));
068        }
069
070        @Override
071        public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
072                        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
073
074                ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
075
076                if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
077                        // MultiValueMap
078                        Class<?> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve();
079                        if (valueType == MultipartFile.class) {
080                                MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
081                                return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0));
082                        }
083                        else if (valueType == Part.class) {
084                                HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
085                                if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
086                                        Collection<Part> parts = servletRequest.getParts();
087                                        LinkedMultiValueMap<String, Part> result = new LinkedMultiValueMap<>(parts.size());
088                                        for (Part part : parts) {
089                                                result.add(part.getName(), part);
090                                        }
091                                        return result;
092                                }
093                                return new LinkedMultiValueMap<>(0);
094                        }
095                        else {
096                                Map<String, String[]> parameterMap = webRequest.getParameterMap();
097                                MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());
098                                parameterMap.forEach((key, values) -> {
099                                        for (String value : values) {
100                                                result.add(key, value);
101                                        }
102                                });
103                                return result;
104                        }
105                }
106
107                else {
108                        // Regular Map
109                        Class<?> valueType = resolvableType.asMap().getGeneric(1).resolve();
110                        if (valueType == MultipartFile.class) {
111                                MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
112                                return (multipartRequest != null ? multipartRequest.getFileMap() : new LinkedHashMap<>(0));
113                        }
114                        else if (valueType == Part.class) {
115                                HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
116                                if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
117                                        Collection<Part> parts = servletRequest.getParts();
118                                        LinkedHashMap<String, Part> result = new LinkedHashMap<>(parts.size());
119                                        for (Part part : parts) {
120                                                if (!result.containsKey(part.getName())) {
121                                                        result.put(part.getName(), part);
122                                                }
123                                        }
124                                        return result;
125                                }
126                                return new LinkedHashMap<>(0);
127                        }
128                        else {
129                                Map<String, String[]> parameterMap = webRequest.getParameterMap();
130                                Map<String, String> result = new LinkedHashMap<>(parameterMap.size());
131                                parameterMap.forEach((key, values) -> {
132                                        if (values.length > 0) {
133                                                result.put(key, values[0]);
134                                        }
135                                });
136                                return result;
137                        }
138                }
139        }
140
141}