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}