Description
Nick Williams opened SPR-10591 and commented
Spring's support for javax.servlet.http.Part
vs. org.springframework.web.multipart.MultipartFile
is very inconsistent. You cannot use the two the same ways, which the documentation seems to suggest should be the case.
I'm using @Configuration
with @EnableWebMVC
. I define the following bean in my configuration:
@Bean
public MultipartResolver multipartResolver()
{
return new StandardServletMultipartResolver();
}
The following eight scenarios demonstrate the inconsistencies. It seems to me that all eight scenarios should work, but three of them do not.
Scenario 1
I have a controller handler method with a @RequestPart
MultipartFile
parameter. Everything works fine, and when the method is called the uploaded file is supplied to the method.
Scenario 2
I have a controller handler method with a @RequestPart
List<MultipartFile>
parameter. Everything works fine, and when the method is called the uploaded file or files are supplied to the method (I tired uploading a single file and multiple files; both worked).
Scenario 3
I have a controller handler method with a @RequestPart
Part
parameter. Everything works fine, and when the method is called the uploaded file is supplied to the method.
Scenario 4
I have a controller handler method with a @RequestPart
List<Part>
parameter. I tired uploading a single file and multiple files, but neither worked. Spring replied to the request with error code 415: "The server refused this request because the request entity is in a format not supported by the requested resource for the requested method." The following debug information appeared in the log for this request:
13:17:07.352 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:823): DispatcherServlet with name 'springDispatcher' processing POST request for [/support/ticket/create]
13:17:07.376 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:222): Looking up handler method for path /ticket/create
13:17:07.376 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:229): Returning handler method [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form,java.util.List<javax.servlet.http.Part>) throws java.io.IOException]
13:17:07.376 [http-nio-8080-exec-3] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:249): Returning cached instance of singleton bean 'ticketController'
13:17:07.561 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134): Resolving exception from handler [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form,java.util.List<javax.servlet.http.Part>) throws java.io.IOException]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
13:17:07.565 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134): Resolving exception from handler [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form,java.util.List<javax.servlet.http.Part>) throws java.io.IOException]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
13:17:07.566 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134): Resolving exception from handler [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form,java.util.List<javax.servlet.http.Part>) throws java.io.IOException]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
13:17:07.567 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:999): Null ModelAndView returned to DispatcherServlet with name 'springDispatcher': assuming HandlerAdapter completed request handling
13:17:07.567 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:976): Successfully completed request
Scenario 5
I have a controller handler method with a Form
command-object parameter. The Form
(POJO) has a field in it of type MultipartFile
. Everything works fine, and when the method is called the uploaded file is supplied to the method.
Scenario 6
I have a controller handler method with a Form
command-object parameter. The Form
has a field in it of type List<MultipartFile>
. Everything works fine, and when the method is called the uploaded file or files are supplied to the method (I tired uploading a single file and multiple files; both worked).
Scenario 7
I have a controller handler method with a Form
command-object parameter. The Form
(POJO) has a field in it of type Part
. I cannot upload a file. Spring replied to the request with error code 400: "The request sent by the client was syntactically incorrect." The following debug information appeared in the log for this request:
13:37:27.657 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.DispatcherServlet org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:823): DispatcherServlet with name 'springDispatcher' processing POST request for [/support/ticket/create]
13:37:27.685 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:222): Looking up handler method for path /ticket/create
13:37:27.686 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:229): Returning handler method [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form) throws java.io.IOException]
13:37:27.686 [http-nio-8080-exec-4] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:249): Returning cached instance of singleton bean 'ticketController'
13:37:27.690 [http-nio-8080-exec-4] DEBUG org.springframework.beans.BeanUtils org.springframework.beans.BeanUtils.findEditorByConvention(BeanUtils.java:443): No property editor [javax.servlet.http.PartEditor] found for type javax.servlet.http.Part according to 'Editor' suffix convention
13:37:27.693 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134): Resolving exception from handler [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form) throws java.io.IOException]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'form' on field 'attachments': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@8e3de73]; codes [typeMismatch.form.attachments,typeMismatch.attachments,typeMismatch.javax.servlet.http.Part,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [form.attachments,attachments]; arguments []; default message [attachments]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'javax.servlet.http.Part' for property 'attachments'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile] to required type [javax.servlet.http.Part] for property 'attachments': no matching editors or conversion strategy found]
13:37:27.696 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134): Resolving exception from handler [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form) throws java.io.IOException]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'form' on field 'attachments': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@8e3de73]; codes [typeMismatch.form.attachments,typeMismatch.attachments,typeMismatch.javax.servlet.http.Part,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [form.attachments,attachments]; arguments []; default message [attachments]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'javax.servlet.http.Part' for property 'attachments'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile] to required type [javax.servlet.http.Part] for property 'attachments': no matching editors or conversion strategy found]
13:37:27.696 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134): Resolving exception from handler [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form) throws java.io.IOException]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'form' on field 'attachments': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@8e3de73]; codes [typeMismatch.form.attachments,typeMismatch.attachments,typeMismatch.javax.servlet.http.Part,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [form.attachments,attachments]; arguments []; default message [attachments]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'javax.servlet.http.Part' for property 'attachments'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile] to required type [javax.servlet.http.Part] for property 'attachments': no matching editors or conversion strategy found]
13:37:27.700 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.DispatcherServlet org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:999): Null ModelAndView returned to DispatcherServlet with name 'springDispatcher': assuming HandlerAdapter completed request handling
13:37:27.701 [http-nio-8080-exec-4] DEBUG org.springframework.web.servlet.DispatcherServlet org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:976): Successfully completed request
Scenario 7
I have a controller handler method with a Form
command-object parameter. The Form
(POJO) has a field in it of type List<Part>
. I tired uploading a single file and multiple files, but neither worked. Spring replied to the request with error code 400: "The request sent by the client was syntactically incorrect." The following debug information appeared in the log for this request:
13:39:27.414 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:823): DispatcherServlet with name 'springDispatcher' processing POST request for [/support/ticket/create]
13:39:27.443 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:222): Looking up handler method for path /ticket/create
13:39:27.447 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:229): Returning handler method [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form) throws java.io.IOException]
13:39:27.448 [http-nio-8080-exec-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:249): Returning cached instance of singleton bean 'ticketController'
13:39:27.488 [http-nio-8080-exec-1] DEBUG org.springframework.beans.BeanUtils org.springframework.beans.BeanUtils.findEditorByConvention(BeanUtils.java:443): No property editor [javax.servlet.http.PartEditor] found for type javax.servlet.http.Part according to 'Editor' suffix convention
13:39:27.491 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134): Resolving exception from handler [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form) throws java.io.IOException]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'form' on field 'attachments': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@3f7c6df8]; codes [typeMismatch.form.attachments,typeMismatch.attachments,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [form.attachments,attachments]; arguments []; default message [attachments]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.util.List' for property 'attachments'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile] to required type [javax.servlet.http.Part] for property 'attachments[0]': no matching editors or conversion strategy found]
13:39:27.494 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134): Resolving exception from handler [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form) throws java.io.IOException]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'form' on field 'attachments': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@3f7c6df8]; codes [typeMismatch.form.attachments,typeMismatch.attachments,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [form.attachments,attachments]; arguments []; default message [attachments]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.util.List' for property 'attachments'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile] to required type [javax.servlet.http.Part] for property 'attachments[0]': no matching editors or conversion strategy found]
13:39:27.494 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:134): Resolving exception from handler [public org.springframework.web.servlet.View com.wrox.site.TicketController.create(javax.servlet.http.HttpSession,com.wrox.site.TicketController$Form) throws java.io.IOException]: org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'form' on field 'attachments': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@3f7c6df8]; codes [typeMismatch.form.attachments,typeMismatch.attachments,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [form.attachments,attachments]; arguments []; default message [attachments]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.util.List' for property 'attachments'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile] to required type [javax.servlet.http.Part] for property 'attachments[0]': no matching editors or conversion strategy found]
13:39:27.495 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:999): Null ModelAndView returned to DispatcherServlet with name 'springDispatcher': assuming HandlerAdapter completed request handling
13:39:27.496 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:976): Successfully completed request
Affects: 3.1 GA, 3.2 GA, 4.0 M1
Issue Links:
- Part list/array gets resolved to all parts in current request [SPR-13893] #18467 Part list/array gets resolved to all parts in current request
- @RequestPart(...) MultipartFile[] does not work (List<MultipartFile works) [SPR-11353] #15978
@RequestPart
(...) MultipartFile[] does not work (List<MultipartFile works) - MultipartFile argument requires multipart request even when optional (and empty) [SPR-13849] #18422 MultipartFile argument requires multipart request even when optional (and empty)