1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.servlet.mvc.method.annotation;
18
19 import java.io.IOException;
20 import java.lang.reflect.Method;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.List;
24 import javax.servlet.http.Part;
25 import javax.validation.Valid;
26 import javax.validation.constraints.NotNull;
27
28 import org.hamcrest.Matchers;
29 import org.junit.Before;
30 import org.junit.Test;
31
32 import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
33 import org.springframework.core.MethodParameter;
34 import org.springframework.http.MediaType;
35 import org.springframework.http.converter.HttpMessageConverter;
36 import org.springframework.mock.web.test.MockHttpServletRequest;
37 import org.springframework.mock.web.test.MockHttpServletResponse;
38 import org.springframework.mock.web.test.MockMultipartFile;
39 import org.springframework.mock.web.test.MockMultipartHttpServletRequest;
40 import org.springframework.mock.web.test.MockPart;
41 import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
42 import org.springframework.web.bind.MethodArgumentNotValidException;
43 import org.springframework.web.bind.WebDataBinder;
44 import org.springframework.web.bind.annotation.RequestParam;
45 import org.springframework.web.bind.annotation.RequestPart;
46 import org.springframework.web.bind.support.WebDataBinderFactory;
47 import org.springframework.web.context.request.NativeWebRequest;
48 import org.springframework.web.context.request.ServletWebRequest;
49 import org.springframework.web.method.support.ModelAndViewContainer;
50 import org.springframework.web.multipart.MultipartException;
51 import org.springframework.web.multipart.MultipartFile;
52 import org.springframework.web.multipart.support.MissingServletRequestPartException;
53 import org.springframework.web.multipart.support.RequestPartServletServerHttpRequest;
54
55 import static org.junit.Assert.*;
56 import static org.mockito.BDDMockito.*;
57
58
59
60
61
62
63
64 public class RequestPartMethodArgumentResolverTests {
65
66 private RequestPartMethodArgumentResolver resolver;
67
68 private HttpMessageConverter<SimpleBean> messageConverter;
69
70 private MultipartFile multipartFile1;
71 private MultipartFile multipartFile2;
72
73 private MethodParameter paramRequestPart;
74 private MethodParameter paramNamedRequestPart;
75 private MethodParameter paramValidRequestPart;
76 private MethodParameter paramMultipartFile;
77 private MethodParameter paramMultipartFileList;
78 private MethodParameter paramMultipartFileArray;
79 private MethodParameter paramInt;
80 private MethodParameter paramMultipartFileNotAnnot;
81 private MethodParameter paramPart;
82 private MethodParameter paramPartList;
83 private MethodParameter paramPartArray;
84 private MethodParameter paramRequestParamAnnot;
85
86 private NativeWebRequest webRequest;
87
88 private MockMultipartHttpServletRequest multipartRequest;
89
90 private MockHttpServletResponse servletResponse;
91
92 @SuppressWarnings("unchecked")
93 @Before
94 public void setUp() throws Exception {
95
96 Method method = getClass().getMethod("handle", SimpleBean.class, SimpleBean.class,
97 SimpleBean.class, MultipartFile.class, List.class, MultipartFile[].class,
98 Integer.TYPE, MultipartFile.class, Part.class, List.class,
99 Part[].class, MultipartFile.class);
100
101 paramRequestPart = new MethodParameter(method, 0);
102 paramRequestPart.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
103 paramNamedRequestPart = new MethodParameter(method, 1);
104 paramValidRequestPart = new MethodParameter(method, 2);
105 paramMultipartFile = new MethodParameter(method, 3);
106 paramMultipartFileList = new MethodParameter(method, 4);
107 paramMultipartFileArray = new MethodParameter(method, 5);
108 paramInt = new MethodParameter(method, 6);
109 paramMultipartFileNotAnnot = new MethodParameter(method, 7);
110 paramMultipartFileNotAnnot.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
111 paramPart = new MethodParameter(method, 8);
112 paramPart.initParameterNameDiscovery(new LocalVariableTableParameterNameDiscoverer());
113 paramPartList = new MethodParameter(method, 9);
114 paramPartArray = new MethodParameter(method, 10);
115 paramRequestParamAnnot = new MethodParameter(method, 11);
116
117 messageConverter = mock(HttpMessageConverter.class);
118 given(messageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
119
120 resolver = new RequestPartMethodArgumentResolver(Collections.<HttpMessageConverter<?>>singletonList(messageConverter));
121 reset(messageConverter);
122
123 multipartFile1 = new MockMultipartFile("requestPart", "", "text/plain", (byte[]) null);
124 multipartFile2 = new MockMultipartFile("requestPart", "", "text/plain", (byte[]) null);
125 multipartRequest = new MockMultipartHttpServletRequest();
126 multipartRequest.addFile(multipartFile1);
127 multipartRequest.addFile(multipartFile2);
128 servletResponse = new MockHttpServletResponse();
129 webRequest = new ServletWebRequest(multipartRequest, servletResponse);
130 }
131
132 @Test
133 public void supportsParameter() {
134 assertTrue("RequestPart parameter not supported", resolver.supportsParameter(paramRequestPart));
135 assertTrue("MultipartFile parameter not supported", resolver.supportsParameter(paramMultipartFileNotAnnot));
136 assertTrue("Part parameter not supported", resolver.supportsParameter(paramPart));
137 assertTrue("List<Part> parameter not supported", resolver.supportsParameter(paramPartList));
138 assertTrue("Part[] parameter not supported", resolver.supportsParameter(paramPartArray));
139 assertTrue("MultipartFile parameter not supported", resolver.supportsParameter(paramMultipartFile));
140 assertTrue("List<MultipartFile> parameter not supported", resolver.supportsParameter(paramMultipartFileList));
141 assertTrue("MultipartFile[] parameter not supported", resolver.supportsParameter(paramMultipartFileArray));
142 assertFalse("non-RequestPart parameter supported", resolver.supportsParameter(paramInt));
143 assertFalse("@RequestParam args not supported", resolver.supportsParameter(paramRequestParamAnnot));
144 }
145
146 @Test
147 public void resolveMultipartFile() throws Exception {
148 Object actual = resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
149 assertNotNull(actual);
150 assertSame(multipartFile1, actual);
151 }
152
153 @Test
154 public void resolveMultipartFileList() throws Exception {
155 Object actual = resolver.resolveArgument(paramMultipartFileList, null, webRequest, null);
156 assertNotNull(actual);
157 assertTrue(actual instanceof List);
158 assertEquals(Arrays.asList(multipartFile1, multipartFile2), actual);
159 }
160
161 @Test
162 public void resolveMultipartFileArray() throws Exception {
163 Object actual = resolver.resolveArgument(paramMultipartFileArray, null, webRequest, null);
164 assertNotNull(actual);
165 assertTrue(actual instanceof MultipartFile[]);
166 MultipartFile[] parts = (MultipartFile[]) actual;
167 assertEquals(parts[0], multipartFile1);
168 assertEquals(parts[1], multipartFile2);
169 }
170
171 @Test
172 public void resolveMultipartFileNotAnnotArgument() throws Exception {
173 MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
174 MultipartFile expected = new MockMultipartFile("multipartFileNotAnnot", "Hello World".getBytes());
175 request.addFile(expected);
176 webRequest = new ServletWebRequest(request);
177
178 Object result = resolver.resolveArgument(paramMultipartFileNotAnnot, null, webRequest, null);
179
180 assertTrue(result instanceof MultipartFile);
181 assertEquals("Invalid result", expected, result);
182 }
183
184 @Test
185 public void resolvePartArgument() throws Exception {
186 MockPart expected = new MockPart("part", "Hello World".getBytes());
187 MockHttpServletRequest request = new MockHttpServletRequest();
188 request.setMethod("POST");
189 request.setContentType("multipart/form-data");
190 request.addPart(expected);
191 webRequest = new ServletWebRequest(request);
192
193 Object result = resolver.resolveArgument(paramPart, null, webRequest, null);
194
195 assertTrue(result instanceof Part);
196 assertEquals("Invalid result", expected, result);
197 }
198
199 @Test
200 public void resolvePartListArgument() throws Exception {
201 MockPart part1 = new MockPart("requestPart1", "Hello World 1".getBytes());
202 MockPart part2 = new MockPart("requestPart2", "Hello World 2".getBytes());
203 MockHttpServletRequest request = new MockHttpServletRequest();
204 request.setMethod("POST");
205 request.setContentType("multipart/form-data");
206 request.addPart(part1);
207 request.addPart(part2);
208 webRequest = new ServletWebRequest(request);
209
210 Object result = resolver.resolveArgument(paramPartList, null, webRequest, null);
211
212 assertTrue(result instanceof List);
213 assertEquals(Arrays.asList(part1, part2), result);
214 }
215
216 @Test
217 public void resolvePartArrayArgument() throws Exception {
218 MockPart part1 = new MockPart("requestPart1", "Hello World 1".getBytes());
219 MockPart part2 = new MockPart("requestPart2", "Hello World 2".getBytes());
220 MockHttpServletRequest request = new MockHttpServletRequest();
221 request.setMethod("POST");
222 request.setContentType("multipart/form-data");
223 request.addPart(part1);
224 request.addPart(part2);
225 webRequest = new ServletWebRequest(request);
226
227 Object result = resolver.resolveArgument(paramPartArray, null, webRequest, null);
228
229 assertTrue(result instanceof Part[]);
230 Part[] parts = (Part[]) result;
231 assertThat(parts, Matchers.arrayWithSize(2));
232 assertEquals(parts[0], part1);
233 assertEquals(parts[1], part2);
234 }
235
236 @Test
237 public void resolveRequestPart() throws Exception {
238 testResolveArgument(new SimpleBean("foo"), paramRequestPart);
239 }
240
241 @Test
242 public void resolveNamedRequestPart() throws Exception {
243 testResolveArgument(new SimpleBean("foo"), paramNamedRequestPart);
244 }
245
246 @Test
247 public void resolveRequestPartNotValid() throws Exception {
248 try {
249 testResolveArgument(new SimpleBean(null), paramValidRequestPart);
250 fail("Expected exception");
251 } catch (MethodArgumentNotValidException e) {
252 assertEquals("requestPart", e.getBindingResult().getObjectName());
253 assertEquals(1, e.getBindingResult().getErrorCount());
254 assertNotNull(e.getBindingResult().getFieldError("name"));
255 }
256 }
257
258 @Test
259 public void resolveRequestPartValid() throws Exception {
260 testResolveArgument(new SimpleBean("foo"), paramNamedRequestPart);
261 }
262
263 @Test
264 public void resolveRequestPartRequired() throws Exception {
265 try {
266 testResolveArgument(null, paramValidRequestPart);
267 fail("Expected exception");
268 } catch (MissingServletRequestPartException e) {
269 assertEquals("requestPart", e.getRequestPartName());
270 }
271 }
272
273 @Test
274 public void resolveRequestPartNotRequired() throws Exception {
275 testResolveArgument(new SimpleBean("foo"), paramValidRequestPart);
276 }
277
278 @Test(expected=MultipartException.class)
279 public void isMultipartRequest() throws Exception {
280 MockHttpServletRequest request = new MockHttpServletRequest();
281 resolver.resolveArgument(paramMultipartFile, new ModelAndViewContainer(), new ServletWebRequest(request), null);
282 fail("Expected exception");
283 }
284
285
286
287 @Test
288 public void isMultipartRequestPut() throws Exception {
289 this.multipartRequest.setMethod("PUT");
290 Object actual = resolver.resolveArgument(paramMultipartFile, null, webRequest, null);
291 assertNotNull(actual);
292 assertSame(multipartFile1, actual);
293 }
294
295 private void testResolveArgument(SimpleBean argValue, MethodParameter parameter) throws IOException, Exception {
296 MediaType contentType = MediaType.TEXT_PLAIN;
297
298 given(messageConverter.canRead(SimpleBean.class, contentType)).willReturn(true);
299 given(messageConverter.read(eq(SimpleBean.class), isA(RequestPartServletServerHttpRequest.class))).willReturn(argValue);
300
301 ModelAndViewContainer mavContainer = new ModelAndViewContainer();
302 Object actualValue = resolver.resolveArgument(parameter, mavContainer, webRequest, new ValidatingBinderFactory());
303
304 assertEquals("Invalid argument value", argValue, actualValue);
305 assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled());
306 }
307
308 private static class SimpleBean {
309
310 @NotNull
311 private final String name;
312
313 public SimpleBean(String name) {
314 this.name = name;
315 }
316
317 @SuppressWarnings("unused")
318 public String getName() {
319 return name;
320 }
321 }
322
323 private final class ValidatingBinderFactory implements WebDataBinderFactory {
324 @Override
325 public WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName) throws Exception {
326 LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
327 validator.afterPropertiesSet();
328 WebDataBinder dataBinder = new WebDataBinder(target, objectName);
329 dataBinder.setValidator(validator);
330 return dataBinder;
331 }
332 }
333
334 public void handle(@RequestPart SimpleBean requestPart,
335 @RequestPart(value="requestPart", required=false) SimpleBean namedRequestPart,
336 @Valid @RequestPart("requestPart") SimpleBean validRequestPart,
337 @RequestPart("requestPart") MultipartFile multipartFile,
338 @RequestPart("requestPart") List<MultipartFile> multipartFileList,
339 @RequestPart("requestPart") MultipartFile[] multipartFileArray,
340 int i,
341 MultipartFile multipartFileNotAnnot,
342 Part part,
343 @RequestPart("part") List<Part> partList,
344 @RequestPart("part") Part[] partArray,
345 @RequestParam MultipartFile requestParamAnnot) {
346 }
347
348 }