diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/PortletRequestDataBinder.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/PortletRequestDataBinder.java index abeff33cf66..26c3a5b55fa 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/PortletRequestDataBinder.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/bind/PortletRequestDataBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -107,7 +107,7 @@ public class PortletRequestDataBinder extends WebDataBinder { MutablePropertyValues mpvs = new PortletRequestParameterPropertyValues(request); if (request instanceof MultipartRequest) { MultipartRequest multipartRequest = (MultipartRequest) request; - bindMultipartFiles(multipartRequest.getFileMap(), mpvs); + bindMultipart(multipartRequest.getMultiFileMap(), mpvs); } doBind(mpvs); } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java index f17a5c9eabe..aceec7aa118 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/ServletAnnotationControllerTests.java @@ -86,6 +86,8 @@ import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.mock.web.MockMultipartHttpServletRequest; import org.springframework.mock.web.MockServletConfig; import org.springframework.mock.web.MockServletContext; import org.springframework.oxm.jaxb.Jaxb2Marshaller; @@ -119,6 +121,7 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.support.GenericWebApplicationContext; +import org.springframework.web.multipart.support.StringMultipartFileEditor; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.View; @@ -1699,6 +1702,43 @@ public class ServletAnnotationControllerTests { assertEquals("test-{foo=bar}", response.getContentAsString()); } + @Test + public void multipartFileAsSingleString() throws Exception { + initServlet(MultipartController.class); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + request.setRequestURI("/singleString"); + request.addFile(new MockMultipartFile("content", "Juergen".getBytes())); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Juergen", response.getContentAsString()); + } + + @Test + public void multipartFileAsStringArray() throws Exception { + initServlet(MultipartController.class); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + request.setRequestURI("/stringArray"); + request.addFile(new MockMultipartFile("content", "Juergen".getBytes())); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Juergen", response.getContentAsString()); + } + + @Test + public void multipartFilesAsStringArray() throws Exception { + initServlet(MultipartController.class); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + request.setRequestURI("/stringArray"); + request.addFile(new MockMultipartFile("content", "Juergen".getBytes())); + request.addFile(new MockMultipartFile("content", "Eva".getBytes())); + MockHttpServletResponse response = new MockHttpServletResponse(); + servlet.service(request, response); + assertEquals("Juergen,Eva", response.getContentAsString()); + } + /* * Controllers @@ -2922,4 +2962,23 @@ public class ServletAnnotationControllerTests { } + @Controller + public static class MultipartController { + + @InitBinder + public void initBinder(WebDataBinder binder) { + binder.registerCustomEditor(String.class, new StringMultipartFileEditor()); + } + + @RequestMapping("/singleString") + public void processMultipart(@RequestParam("content") String content, HttpServletResponse response) throws IOException { + response.getWriter().write(content); + } + + @RequestMapping("/stringArray") + public void processMultipart(@RequestParam("content") String[] content, HttpServletResponse response) throws IOException { + response.getWriter().write(StringUtils.arrayToCommaDelimitedString(content)); + } + } + } diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java b/org.springframework.web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java index 0a72c0c9258..2edc899cfa9 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java +++ b/org.springframework.web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -105,7 +105,7 @@ public class ServletRequestDataBinder extends WebDataBinder { MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request); if (request instanceof MultipartRequest) { MultipartRequest multipartRequest = (MultipartRequest) request; - bindMultipartFiles(multipartRequest.getFileMap(), mpvs); + bindMultipart(multipartRequest.getMultiFileMap(), mpvs); } doBind(mpvs); } diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/WebDataBinder.java b/org.springframework.web/src/main/java/org/springframework/web/bind/WebDataBinder.java index 73644abdae1..15d799210ec 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/bind/WebDataBinder.java +++ b/org.springframework.web/src/main/java/org/springframework/web/bind/WebDataBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -17,6 +17,7 @@ package org.springframework.web.bind; import java.lang.reflect.Array; +import java.util.List; import java.util.Map; import org.springframework.beans.MutablePropertyValues; @@ -276,7 +277,10 @@ public class WebDataBinder extends DataBinder { * @param mpvs the property values to be bound (can be modified) * @see org.springframework.web.multipart.MultipartFile * @see #setBindEmptyMultipartFiles + * @deprecated as of Spring 3.0, in favor of {@link #bindMultipart} which binds + * all multipart files, even if more than one sent for the same name */ + @Deprecated protected void bindMultipartFiles(Map multipartFiles, MutablePropertyValues mpvs) { for (Map.Entry entry : multipartFiles.entrySet()) { String key = entry.getKey(); @@ -287,4 +291,30 @@ public class WebDataBinder extends DataBinder { } } + /** + * Bind all multipart files contained in the given request, if any + * (in case of a multipart request). + *

Multipart files will only be added to the property values if they + * are not empty or if we're configured to bind empty multipart files too. + * @param multipartFiles Map of field name String to MultipartFile object + * @param mpvs the property values to be bound (can be modified) + * @see org.springframework.web.multipart.MultipartFile + * @see #setBindEmptyMultipartFiles + */ + protected void bindMultipart(Map> multipartFiles, MutablePropertyValues mpvs) { + for (Map.Entry> entry : multipartFiles.entrySet()) { + String key = entry.getKey(); + List values = entry.getValue(); + if (values.size() == 1) { + MultipartFile value = values.get(0); + if (isBindEmptyMultipartFiles() || !value.isEmpty()) { + mpvs.add(key, value); + } + } + else { + mpvs.add(key, values); + } + } + } + } diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java index d6785884767..b5116ed04a3 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java +++ b/org.springframework.web/src/main/java/org/springframework/web/bind/annotation/support/HandlerMethodInvoker.java @@ -78,6 +78,7 @@ import org.springframework.web.bind.support.WebBindingInitializer; import org.springframework.web.bind.support.WebRequestDataBinder; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartRequest; /** @@ -153,7 +154,7 @@ public class HandlerMethodInvoker { if (debug) { logger.debug("Invoking model attribute method: " + attributeMethodToInvoke); } - String attrName = AnnotationUtils.findAnnotation(attributeMethodToInvoke, ModelAttribute.class).value(); + String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value(); if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) { continue; } @@ -381,7 +382,7 @@ public class HandlerMethodInvoker { boolean debug = logger.isDebugEnabled(); for (Method initBinderMethod : initBinderMethods) { Method methodToInvoke = BridgeMethodResolver.findBridgedMethod(initBinderMethod); - String[] targetNames = AnnotationUtils.findAnnotation(methodToInvoke, InitBinder.class).value(); + String[] targetNames = AnnotationUtils.findAnnotation(initBinderMethod, InitBinder.class).value(); if (targetNames.length == 0 || Arrays.asList(targetNames).contains(attrName)) { Object[] initBinderArgs = resolveInitBinderArguments(handler, methodToInvoke, binder, webRequest); @@ -481,15 +482,25 @@ public class HandlerMethodInvoker { Object paramValue = null; MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class); if (multipartRequest != null) { - paramValue = multipartRequest.getFile(paramName); + List files = multipartRequest.getFiles(paramName); + if (!files.isEmpty()) { + if (files.size() == 1 && !paramType.isArray() && !Collection.class.isAssignableFrom(paramType)) { + paramValue = files.get(0); + } + else { + paramValue = files; + } + } } if (paramValue == null) { String[] paramValues = webRequest.getParameterValues(paramName); - if (paramValues != null && !paramType.isArray()) { - paramValue = (paramValues.length == 1 ? paramValues[0] : paramValues); - } - else { - paramValue = paramValues; + if (paramValues != null) { + if (paramValues.length == 1 && !paramType.isArray() && !Collection.class.isAssignableFrom(paramType)) { + paramValue = paramValues[0]; + } + else { + paramValue = paramValues; + } } } if (paramValue == null) { diff --git a/org.springframework.web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java b/org.springframework.web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java index 062abd54e0a..dd7d69ac48a 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java +++ b/org.springframework.web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java @@ -103,7 +103,7 @@ public class WebRequestDataBinder extends WebDataBinder { if (request instanceof NativeWebRequest) { MultipartRequest multipartRequest = ((NativeWebRequest) request).getNativeRequest(MultipartRequest.class); if (multipartRequest != null) { - bindMultipartFiles(multipartRequest.getFileMap(), mpvs); + bindMultipart(multipartRequest.getMultiFileMap(), mpvs); } } doBind(mpvs); diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartRequest.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartRequest.java index f90ec14a927..857b947e2d8 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartRequest.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -64,7 +64,6 @@ public interface MultipartRequest { * Return a {@link java.util.Map} of the multipart files contained in this request. * @return a map containing the parameter names as keys, and the * {@link MultipartFile} objects as values - * @see MultipartFile */ Map getFileMap(); @@ -72,7 +71,6 @@ public interface MultipartRequest { * Return a {@link MultiValueMap} of the multipart files contained in this request. * @return a map containing the parameter names as keys, and a list of * {@link MultipartFile} objects as values - * @see MultipartFile * @since 3.0 */ MultiValueMap getMultiFileMap(); diff --git a/org.springframework.web/src/test/java/org/springframework/web/bind/support/WebRequestDataBinderTests.java b/org.springframework.web/src/test/java/org/springframework/web/bind/support/WebRequestDataBinderTests.java index 9091c9c2f7e..4485aecee67 100644 --- a/org.springframework.web/src/test/java/org/springframework/web/bind/support/WebRequestDataBinderTests.java +++ b/org.springframework.web/src/test/java/org/springframework/web/bind/support/WebRequestDataBinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -29,8 +29,11 @@ import org.springframework.beans.PropertyValue; import org.springframework.beans.PropertyValues; import org.springframework.beans.TestBean; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.mock.web.MockMultipartHttpServletRequest; import org.springframework.web.bind.ServletRequestParameterPropertyValues; import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.multipart.support.StringMultipartFileEditor; /** * @author Juergen Hoeller @@ -187,6 +190,46 @@ public class WebRequestDataBinderTests { assertEquals(MyEnum.FOO, target.getMyEnum()); } + @Test + public void testMultipartFileAsString() { + TestBean target = new TestBean(); + WebRequestDataBinder binder = new WebRequestDataBinder(target); + binder.registerCustomEditor(String.class, new StringMultipartFileEditor()); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + request.addFile(new MockMultipartFile("name", "Juergen".getBytes())); + binder.bind(new ServletWebRequest(request)); + assertEquals("Juergen", target.getName()); + } + + @Test + public void testMultipartFileAsStringArray() { + TestBean target = new TestBean(); + WebRequestDataBinder binder = new WebRequestDataBinder(target); + binder.registerCustomEditor(String.class, new StringMultipartFileEditor()); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + request.addFile(new MockMultipartFile("stringArray", "Juergen".getBytes())); + binder.bind(new ServletWebRequest(request)); + assertEquals(1, target.getStringArray().length); + assertEquals("Juergen", target.getStringArray()[0]); + } + + @Test + public void testMultipartFilesAsStringArray() { + TestBean target = new TestBean(); + WebRequestDataBinder binder = new WebRequestDataBinder(target); + binder.registerCustomEditor(String.class, new StringMultipartFileEditor()); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + request.addFile(new MockMultipartFile("stringArray", "Juergen".getBytes())); + request.addFile(new MockMultipartFile("stringArray", "Eva".getBytes())); + binder.bind(new ServletWebRequest(request)); + assertEquals(2, target.getStringArray().length); + assertEquals("Juergen", target.getStringArray()[0]); + assertEquals("Eva", target.getStringArray()[1]); + } + @Test public void testNoPrefix() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); @@ -213,26 +256,6 @@ public class WebRequestDataBinderTests { doTestTony(pvs); } - @Test - public void testNoParameters() throws Exception { - MockHttpServletRequest request = new MockHttpServletRequest(); - ServletRequestParameterPropertyValues pvs = new ServletRequestParameterPropertyValues(request); - assertTrue("Found no parameters", pvs.getPropertyValues().length == 0); - } - - @Test - public void testMultipleValuesForParameter() throws Exception { - MockHttpServletRequest request = new MockHttpServletRequest(); - String[] original = new String[] {"Tony", "Rod"}; - request.addParameter("forname", original); - - ServletRequestParameterPropertyValues pvs = new ServletRequestParameterPropertyValues(request); - assertTrue("Found 1 parameter", pvs.getPropertyValues().length == 1); - assertTrue("Found array value", pvs.getPropertyValue("forname").getValue() instanceof String[]); - String[] values = (String[]) pvs.getPropertyValue("forname").getValue(); - assertEquals("Correct values", Arrays.asList(values), Arrays.asList(original)); - } - /** * Must contain: forname=Tony surname=Blair age=50 */ @@ -258,6 +281,26 @@ public class WebRequestDataBinderTests { assertTrue("Map size is 0", m.size() == 0); } + @Test + public void testNoParameters() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + ServletRequestParameterPropertyValues pvs = new ServletRequestParameterPropertyValues(request); + assertTrue("Found no parameters", pvs.getPropertyValues().length == 0); + } + + @Test + public void testMultipleValuesForParameter() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + String[] original = new String[] {"Tony", "Rod"}; + request.addParameter("forname", original); + + ServletRequestParameterPropertyValues pvs = new ServletRequestParameterPropertyValues(request); + assertTrue("Found 1 parameter", pvs.getPropertyValues().length == 1); + assertTrue("Found array value", pvs.getPropertyValue("forname").getValue() instanceof String[]); + String[] values = (String[]) pvs.getPropertyValue("forname").getValue(); + assertEquals("Correct values", Arrays.asList(values), Arrays.asList(original)); + } + public static class EnumHolder {