Support for @RequestParam Map declared with MultipartFile/Part values
Issue: SPR-17405
This commit is contained in:
@@ -16,10 +16,14 @@
|
||||
|
||||
package org.springframework.web.method.annotation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -29,22 +33,29 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
import org.springframework.web.multipart.support.MultipartResolutionDelegate;
|
||||
|
||||
/**
|
||||
* Resolves {@link Map} method arguments annotated with an @{@link RequestParam}
|
||||
* where the annotation does not specify a request parameter name.
|
||||
* See {@link RequestParamMethodArgumentResolver} for resolving {@link Map}
|
||||
* method arguments with a request parameter name.
|
||||
*
|
||||
* <p>The created {@link Map} contains all request parameter name/value pairs.
|
||||
* If the method parameter type is {@link MultiValueMap} instead, the created
|
||||
* map contains all request parameters and all there values for cases where
|
||||
* request parameters have multiple values.
|
||||
* <p>The created {@link Map} contains all request parameter name/value pairs,
|
||||
* or all multipart files for a given parameter name if specifically declared
|
||||
* with {@link MultipartFile} as the value type. If the method parameter type is
|
||||
* {@link MultiValueMap} instead, the created map contains all request parameters
|
||||
* and all their values for cases where request parameters have multiple values
|
||||
* (or multiple multipart files of the same name).
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see RequestParamMethodArgumentResolver
|
||||
* @see HttpServletRequest#getParameterMap()
|
||||
* @see MultipartRequest#getMultiFileMap()
|
||||
* @see MultipartRequest#getFileMap()
|
||||
*/
|
||||
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
@@ -59,26 +70,71 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
|
||||
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
|
||||
|
||||
Map<String, String[]> parameterMap = webRequest.getParameterMap();
|
||||
if (MultiValueMap.class.isAssignableFrom(paramType)) {
|
||||
MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());
|
||||
parameterMap.forEach((key, values) -> {
|
||||
for (String value : values) {
|
||||
result.add(key, value);
|
||||
if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
|
||||
// MultiValueMap
|
||||
Class<?> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve();
|
||||
if (valueType == MultipartFile.class) {
|
||||
MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
|
||||
return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0));
|
||||
}
|
||||
else if (valueType == Part.class) {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
|
||||
Collection<Part> parts = servletRequest.getParts();
|
||||
LinkedMultiValueMap<String, Part> result = new LinkedMultiValueMap<>(parts.size());
|
||||
for (Part part : parts) {
|
||||
result.add(part.getName(), part);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
return new LinkedMultiValueMap<>(0);
|
||||
}
|
||||
else {
|
||||
Map<String, String[]> parameterMap = webRequest.getParameterMap();
|
||||
MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());
|
||||
parameterMap.forEach((key, values) -> {
|
||||
for (String value : values) {
|
||||
result.add(key, value);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
Map<String, String> result = new LinkedHashMap<>(parameterMap.size());
|
||||
parameterMap.forEach((key, values) -> {
|
||||
if (values.length > 0) {
|
||||
result.put(key, values[0]);
|
||||
// Regular Map
|
||||
Class<?> valueType = resolvableType.asMap().getGeneric(1).resolve();
|
||||
if (valueType == MultipartFile.class) {
|
||||
MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
|
||||
return (multipartRequest != null ? multipartRequest.getFileMap() : new LinkedHashMap<>(0));
|
||||
}
|
||||
else if (valueType == Part.class) {
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
|
||||
Collection<Part> parts = servletRequest.getParts();
|
||||
LinkedHashMap<String, Part> result = new LinkedHashMap<>(parts.size());
|
||||
for (Part part : parts) {
|
||||
if (!result.containsKey(part.getName())) {
|
||||
result.put(part.getName(), part);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
return new LinkedHashMap<>(0);
|
||||
}
|
||||
else {
|
||||
Map<String, String[]> parameterMap = webRequest.getParameterMap();
|
||||
Map<String, String> result = new LinkedHashMap<>(parameterMap.size());
|
||||
parameterMap.forEach((key, values) -> {
|
||||
if (values.length > 0) {
|
||||
result.put(key, values[0]);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,10 @@ import javax.servlet.http.Part;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
|
||||
/**
|
||||
@@ -44,6 +46,19 @@ public abstract class MultipartResolutionDelegate {
|
||||
public static final Object UNRESOLVABLE = new Object();
|
||||
|
||||
|
||||
@Nullable
|
||||
public static MultipartRequest resolveMultipartRequest(NativeWebRequest webRequest) {
|
||||
MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
|
||||
if (multipartRequest != null) {
|
||||
return multipartRequest;
|
||||
}
|
||||
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
|
||||
if (servletRequest != null && isMultipartContent(servletRequest)) {
|
||||
return new StandardMultipartHttpServletRequest(servletRequest);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isMultipartRequest(HttpServletRequest request) {
|
||||
return (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null ||
|
||||
isMultipartContent(request));
|
||||
@@ -103,13 +118,13 @@ public abstract class MultipartResolutionDelegate {
|
||||
}
|
||||
}
|
||||
else if (Part.class == parameter.getNestedParameterType()) {
|
||||
return (isMultipart ? resolvePart(request, name) : null);
|
||||
return (isMultipart ? request.getPart(name): null);
|
||||
}
|
||||
else if (isPartCollection(parameter)) {
|
||||
return (isMultipart ? resolvePartList(request, name) : null);
|
||||
}
|
||||
else if (isPartArray(parameter)) {
|
||||
return (isMultipart ? resolvePartArray(request, name) : null);
|
||||
return (isMultipart ? resolvePartList(request, name).toArray(new Part[0]) : null);
|
||||
}
|
||||
else {
|
||||
return UNRESOLVABLE;
|
||||
@@ -144,12 +159,8 @@ public abstract class MultipartResolutionDelegate {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Part resolvePart(HttpServletRequest servletRequest, String name) throws Exception {
|
||||
return servletRequest.getPart(name);
|
||||
}
|
||||
|
||||
private static List<Part> resolvePartList(HttpServletRequest servletRequest, String name) throws Exception {
|
||||
Collection<Part> parts = servletRequest.getParts();
|
||||
private static List<Part> resolvePartList(HttpServletRequest request, String name) throws Exception {
|
||||
Collection<Part> parts = request.getParts();
|
||||
List<Part> result = new ArrayList<>(parts.size());
|
||||
for (Part part : parts) {
|
||||
if (part.getName().equals(name)) {
|
||||
@@ -159,15 +170,4 @@ public abstract class MultipartResolutionDelegate {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Part[] resolvePartArray(HttpServletRequest servletRequest, String name) throws Exception {
|
||||
Collection<Part> parts = servletRequest.getParts();
|
||||
List<Part> result = new ArrayList<>(parts.size());
|
||||
for (Part part : parts) {
|
||||
if (part.getName().equals(name)) {
|
||||
result.add(part);
|
||||
}
|
||||
}
|
||||
return result.toArray(new Part[0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -18,74 +18,68 @@ package org.springframework.web.method.annotation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.test.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.test.MockMultipartFile;
|
||||
import org.springframework.mock.web.test.MockMultipartHttpServletRequest;
|
||||
import org.springframework.mock.web.test.MockPart;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.ResolvableMethod;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.web.method.MvcAnnotationPredicates.requestParam;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.web.method.MvcAnnotationPredicates.*;
|
||||
|
||||
/**
|
||||
* Test fixture with {@link RequestParamMapMethodArgumentResolver}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class RequestParamMapMethodArgumentResolverTests {
|
||||
|
||||
private RequestParamMapMethodArgumentResolver resolver;
|
||||
private RequestParamMapMethodArgumentResolver resolver = new RequestParamMapMethodArgumentResolver();
|
||||
|
||||
private NativeWebRequest webRequest;
|
||||
private MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
private NativeWebRequest webRequest = new ServletWebRequest(request, new MockHttpServletResponse());
|
||||
|
||||
private ResolvableMethod testMethod = ResolvableMethod.on(getClass()).named("handle").build();
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
resolver = new RequestParamMapMethodArgumentResolver();
|
||||
|
||||
request = new MockHttpServletRequest();
|
||||
webRequest = new ServletWebRequest(request, new MockHttpServletResponse());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class);
|
||||
MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class, String.class, String.class);
|
||||
assertTrue(resolver.supportsParameter(param));
|
||||
|
||||
param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class);
|
||||
param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class, String.class, String.class);
|
||||
assertTrue(resolver.supportsParameter(param));
|
||||
|
||||
param = this.testMethod.annot(requestParam().name("name")).arg(Map.class);
|
||||
param = this.testMethod.annot(requestParam().name("name")).arg(Map.class, String.class, String.class);
|
||||
assertFalse(resolver.supportsParameter(param));
|
||||
|
||||
param = this.testMethod.annotNotPresent(RequestParam.class).arg(Map.class);
|
||||
param = this.testMethod.annotNotPresent(RequestParam.class).arg(Map.class, String.class, String.class);
|
||||
assertFalse(resolver.supportsParameter(param));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMapArgument() throws Exception {
|
||||
public void resolveMapOfString() throws Exception {
|
||||
String name = "foo";
|
||||
String value = "bar";
|
||||
request.addParameter(name, value);
|
||||
Map<String, String> expected = Collections.singletonMap(name, value);
|
||||
|
||||
MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class);
|
||||
MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class, String.class, String.class);
|
||||
Object result = resolver.resolveArgument(param, null, webRequest, null);
|
||||
|
||||
assertTrue(result instanceof Map);
|
||||
@@ -93,7 +87,7 @@ public class RequestParamMapMethodArgumentResolverTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMultiValueMapArgument() throws Exception {
|
||||
public void resolveMultiValueMapOfString() throws Exception {
|
||||
String name = "foo";
|
||||
String value1 = "bar";
|
||||
String value2 = "baz";
|
||||
@@ -103,19 +97,115 @@ public class RequestParamMapMethodArgumentResolverTests {
|
||||
expected.add(name, value1);
|
||||
expected.add(name, value2);
|
||||
|
||||
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class);
|
||||
MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class, String.class, String.class);
|
||||
Object result = resolver.resolveArgument(param, null, webRequest, null);
|
||||
|
||||
assertTrue(result instanceof MultiValueMap);
|
||||
assertEquals("Invalid result", expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveMapOfMultipartFile() throws Exception {
|
||||
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
||||
MultipartFile expected1 = new MockMultipartFile("mfile", "Hello World".getBytes());
|
||||
MultipartFile expected2 = new MockMultipartFile("other", "Hello World 3".getBytes());
|
||||
request.addFile(expected1);
|
||||
request.addFile(expected2);
|
||||
webRequest = new ServletWebRequest(request);
|
||||
|
||||
MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class, String.class, MultipartFile.class);
|
||||
Object result = resolver.resolveArgument(param, null, webRequest, null);
|
||||
|
||||
assertTrue(result instanceof Map);
|
||||
Map<String, MultipartFile> resultMap = (Map<String, MultipartFile>) result;
|
||||
assertEquals(2, resultMap.size());
|
||||
assertEquals(expected1, resultMap.get("mfile"));
|
||||
assertEquals(expected2, resultMap.get("other"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveMultiValueMapOfMultipartFile() throws Exception {
|
||||
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
|
||||
MultipartFile expected1 = new MockMultipartFile("mfilelist", "Hello World 1".getBytes());
|
||||
MultipartFile expected2 = new MockMultipartFile("mfilelist", "Hello World 2".getBytes());
|
||||
MultipartFile expected3 = new MockMultipartFile("other", "Hello World 3".getBytes());
|
||||
request.addFile(expected1);
|
||||
request.addFile(expected2);
|
||||
request.addFile(expected3);
|
||||
webRequest = new ServletWebRequest(request);
|
||||
|
||||
MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(MultiValueMap.class, String.class, MultipartFile.class);
|
||||
Object result = resolver.resolveArgument(param, null, webRequest, null);
|
||||
|
||||
assertTrue(result instanceof MultiValueMap);
|
||||
MultiValueMap<String, MultipartFile> resultMap = (MultiValueMap<String, MultipartFile>) result;
|
||||
assertEquals(2, resultMap.size());
|
||||
assertEquals(2, resultMap.get("mfilelist").size());
|
||||
assertEquals(expected1, resultMap.get("mfilelist").get(0));
|
||||
assertEquals(expected2, resultMap.get("mfilelist").get(1));
|
||||
assertEquals(1, resultMap.get("other").size());
|
||||
assertEquals(expected3, resultMap.get("other").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveMapOfPart() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setContentType("multipart/form-data");
|
||||
Part expected1 = new MockPart("mfile", "Hello World".getBytes());
|
||||
Part expected2 = new MockPart("other", "Hello World 3".getBytes());
|
||||
request.addPart(expected1);
|
||||
request.addPart(expected2);
|
||||
webRequest = new ServletWebRequest(request);
|
||||
|
||||
MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class, String.class, Part.class);
|
||||
Object result = resolver.resolveArgument(param, null, webRequest, null);
|
||||
|
||||
assertTrue(result instanceof Map);
|
||||
Map<String, Part> resultMap = (Map<String, Part>) result;
|
||||
assertEquals(2, resultMap.size());
|
||||
assertEquals(expected1, resultMap.get("mfile"));
|
||||
assertEquals(expected2, resultMap.get("other"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveMultiValueMapOfPart() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setContentType("multipart/form-data");
|
||||
Part expected1 = new MockPart("mfilelist", "Hello World 1".getBytes());
|
||||
Part expected2 = new MockPart("mfilelist", "Hello World 2".getBytes());
|
||||
Part expected3 = new MockPart("other", "Hello World 3".getBytes());
|
||||
request.addPart(expected1);
|
||||
request.addPart(expected2);
|
||||
request.addPart(expected3);
|
||||
webRequest = new ServletWebRequest(request);
|
||||
|
||||
MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(MultiValueMap.class, String.class, Part.class);
|
||||
Object result = resolver.resolveArgument(param, null, webRequest, null);
|
||||
|
||||
assertTrue(result instanceof MultiValueMap);
|
||||
MultiValueMap<String, Part> resultMap = (MultiValueMap<String, Part>) result;
|
||||
assertEquals(2, resultMap.size());
|
||||
assertEquals(2, resultMap.get("mfilelist").size());
|
||||
assertEquals(expected1, resultMap.get("mfilelist").get(0));
|
||||
assertEquals(expected2, resultMap.get("mfilelist").get(1));
|
||||
assertEquals(1, resultMap.get("other").size());
|
||||
assertEquals(expected3, resultMap.get("other").get(0));
|
||||
}
|
||||
|
||||
|
||||
public void handle(
|
||||
@RequestParam Map<?, ?> param1,
|
||||
@RequestParam MultiValueMap<?, ?> param2,
|
||||
@RequestParam("name") Map<?, ?> param3,
|
||||
Map<?, ?> param4) {
|
||||
@RequestParam Map<String, String> param1,
|
||||
@RequestParam MultiValueMap<String, String> param2,
|
||||
@RequestParam Map<String, MultipartFile> param3,
|
||||
@RequestParam MultiValueMap<String, MultipartFile> param4,
|
||||
@RequestParam Map<String, Part> param5,
|
||||
@RequestParam MultiValueMap<String, Part> param6,
|
||||
@RequestParam("name") Map<String, String> param7,
|
||||
Map<String, String> param8) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1790,10 +1790,11 @@ and others) and is equivalent to `required=false`.
|
||||
| For access to name-value pairs in URI path segments. See <<mvc-ann-matrix-variables>>.
|
||||
|
||||
| `@RequestParam`
|
||||
| For access to the Servlet request parameters. Parameter values are converted to the declared
|
||||
method argument type. See <<mvc-ann-requestparam>>.
|
||||
| For access to the Servlet request parameters, including multipart files. Parameter values
|
||||
are converted to the declared method argument type. See <<mvc-ann-requestparam>> as well
|
||||
as <<mvc-multipart-forms>>.
|
||||
|
||||
Note that use of `@RequestParam` is optional (for example, to set its attributes).
|
||||
Note that use of `@RequestParam` is optional for simple parameter values.
|
||||
See "`Any other argument`", at the end of this table.
|
||||
|
||||
| `@RequestHeader`
|
||||
@@ -1809,12 +1810,12 @@ and others) and is equivalent to `required=false`.
|
||||
argument type by using `HttpMessageConverter` implementations. See <<mvc-ann-requestbody>>.
|
||||
|
||||
| `HttpEntity<B>`
|
||||
| For access to request headers and body. The body is converted with `HttpMessageConverter` implementations.
|
||||
| For access to request headers and body. The body is converted with an `HttpMessageConverter`.
|
||||
See <<mvc-ann-httpentity>>.
|
||||
|
||||
| `@RequestPart`
|
||||
| For access to a part in a `multipart/form-data` request.
|
||||
See <<mvc-multipart-forms>>.
|
||||
| For access to a part in a `multipart/form-data` request, converting the part's body
|
||||
with an `HttpMessageConverter`. See <<mvc-multipart-forms>>.
|
||||
|
||||
| `java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap`
|
||||
| For access to the model that is used in HTML controllers and exposed to templates as
|
||||
@@ -2073,8 +2074,8 @@ To get all matrix variables, you can use a `MultiValueMap`, as the following exa
|
||||
----
|
||||
====
|
||||
|
||||
Note that you need to enable the use of matrix variables. In the MVC Java configuration, you need
|
||||
to set a `UrlPathHelper` with `removeSemicolonContent=false` through
|
||||
Note that you need to enable the use of matrix variables. In the MVC Java configuration,
|
||||
you need to set a `UrlPathHelper` with `removeSemicolonContent=false` through
|
||||
<<mvc-config-path-matching>>. In the MVC XML namespace, you can set
|
||||
`<mvc:annotation-driven enable-matrix-variables="true"/>`.
|
||||
|
||||
@@ -2084,8 +2085,8 @@ to set a `UrlPathHelper` with `removeSemicolonContent=false` through
|
||||
==== `@RequestParam`
|
||||
[.small]#<<web-reactive.adoc#webflux-ann-requestparam,Same as in Spring WebFlux>>#
|
||||
|
||||
You can use the `@RequestParam` annotation to bind Servlet request parameters (that is, query
|
||||
parameters or form data) to a method argument in a controller.
|
||||
You can use the `@RequestParam` annotation to bind Servlet request parameters (that is,
|
||||
query parameters or form data) to a method argument in a controller.
|
||||
|
||||
The following example shows how to do so:
|
||||
|
||||
@@ -2114,17 +2115,20 @@ The following example shows how to do so:
|
||||
====
|
||||
|
||||
By default, method parameters that use this annotation are required, but you can specify that
|
||||
a method parameter is optional by setting the `@RequestParam` annotation's `required` flag to `false`
|
||||
or by declaring the argument with an `java.util.Optional` wrapper.
|
||||
a method parameter is optional by setting the `@RequestParam` annotation's `required` flag to
|
||||
`false` or by declaring the argument with an `java.util.Optional` wrapper.
|
||||
|
||||
Type conversion is automatically applied if the target method parameter type is not
|
||||
`String`. See <<mvc-ann-typeconversion>>.
|
||||
|
||||
When an `@RequestParam` annotation is declared as a `Map<String, String>` or
|
||||
`MultiValueMap<String, String>` argument, the map is populated with all request
|
||||
parameters.
|
||||
Declaring the argument type as an array or list allows for resolving multiple parameter
|
||||
values for the same parameter name.
|
||||
|
||||
Note that use of `@RequestParam` is optional, (for example, to set its attributes).
|
||||
When an `@RequestParam` annotation is declared as a `Map<String, String>` or
|
||||
`MultiValueMap<String, String>`, without a parameter name specified in the annotation,
|
||||
then the map is populated with the request parameter values for each given parameter name.
|
||||
|
||||
Note that use of `@RequestParam` is optional (for example, to set its attributes).
|
||||
By default, any argument that is a simple value type (as determined by
|
||||
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty])
|
||||
and is not resolved by any other argument resolver, is treated as if it were annotated
|
||||
@@ -2534,8 +2538,8 @@ after the redirect, attributes from the "`input`" `FlashMap` are automatically a
|
||||
|
||||
.Matching requests to flash attributes
|
||||
****
|
||||
The concept of flash attributes exists in many other web frameworks and has proven to sometimes be
|
||||
exposed to concurrency issues. This is because, by definition, flash attributes
|
||||
The concept of flash attributes exists in many other web frameworks and has proven to sometimes
|
||||
be exposed to concurrency issues. This is because, by definition, flash attributes
|
||||
are to be stored until the next request. However the very "`next`" request may not be the
|
||||
intended recipient but another asynchronous request (for example, polling or resource requests),
|
||||
in which case the flash attributes are removed too early.
|
||||
@@ -2577,16 +2581,21 @@ public class FileUploadController {
|
||||
// store the bytes somewhere
|
||||
return "redirect:uploadSuccess";
|
||||
}
|
||||
|
||||
return "redirect:uploadFailure";
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: When you use Servlet 3.0 multipart parsing, you can also use `javax.servlet.http.Part`,
|
||||
instead of Spring's `MultipartFile`, as a method argument
|
||||
Declaring the argument type as a `List<MultipartFile>` allows for resolving multiple
|
||||
files for the same parameter name.
|
||||
|
||||
When the `@RequestParam` annotation is declared as a `Map<String, MultipartFile>` or
|
||||
`MultiValueMap<String, MultipartFile>`, without a parameter name specified in the annotation,
|
||||
then the map is populated with the multipart files for each given parameter name.
|
||||
|
||||
NOTE: With Servlet 3.0 multipart parsing, you may also declare `javax.servlet.http.Part`
|
||||
instead of Spring's `MultipartFile`, as a method argument or collection value type.
|
||||
|
||||
You can also use multipart content as part of data binding to a
|
||||
<<mvc-ann-modelattrib-method-args,command object>>. For example, the form field
|
||||
@@ -2604,7 +2613,6 @@ class MyForm {
|
||||
private MultipartFile file;
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
|
||||
@Controller
|
||||
@@ -2612,16 +2620,13 @@ public class FileUploadController {
|
||||
|
||||
@PostMapping("/form")
|
||||
public String handleFormUpload(MyForm form, BindingResult errors) {
|
||||
|
||||
if (!form.getFile().isEmpty()) {
|
||||
byte[] bytes = form.getFile().getBytes();
|
||||
// store the bytes somewhere
|
||||
return "redirect:uploadSuccess";
|
||||
}
|
||||
|
||||
return "redirect:uploadFailure";
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Reference in New Issue
Block a user