使用 Spring WebFlow 2.4.0 上传文件,参数未绑定
File Upload using Spring WebFlow 2.4.0, parameter not binded
我正在使用 Spring Framework 4.1.5、Spring Security 4.0.0.RC2、Spring Webflow 2.4。0.RELEASE 和 Tomcat 8.0.15.
我遵循了 webflow documentation 中的示例,但我无法在我的表单 bean 中获取文件。
形式
<form:form action="${flowExecutionUrl}" method="post" commandName="fileForm" enctype="multipart/form-data">
<form:input type="file" value="" path="multipartFileUpload"/>
<button type="submit" name="_eventId_forward"><spring:message code="signup.forward"/></button>
<sec:csrfInput/>
</form:form>
表单bean
public class FileForm implements Serializable {
private static final long serialVersionUID = 1L;
private transient MultipartFile multipartFileUpload;
public MultipartFile getMultipartFileUpload() {
return multipartFileUpload;
}
public void setMultipartFileUpload(final MultipartFile multipartFileUpload) {
this.multipartFileUpload = multipartFileUpload;
}
}
流量
<view-state id="companyLogo" view="signup/company-logo" model="fileForm">
<var name="fileForm" class="it.openex.pmcommonw.form.FileForm"/>
<transition on="back" to="chooseProfile" bind="false" validate="false"/>
<transition on="forward" to="companyInfo">
<evaluate expression="userCommonBean.uploadImage(fileForm)"/>
</transition>
</view-state>
支持对象
@Component
public class UserCommonBean {
public static void uploadImage(final FileForm fileForm) throws IOException, ServletException {
fileForm.getMultipartFileUpload(); // always null!!!
}
}
multipartResolver
@Bean
public CommonsMultipartResolver filterMultipartResolver() {
final CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(10 * 1024 * 1024);
multipartResolver.setMaxInMemorySize(1048576);
multipartResolver.setDefaultEncoding("UTF-8");
return multipartResolver;
}
webflow 配置
@Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {
@Autowired
TilesViewResolver viewResolver;
@Bean
public FlowDefinitionRegistry flowRegistry() {
return getFlowDefinitionRegistryBuilder()
.setFlowBuilderServices(flowBuilderServices())
.setBasePath("/WEB-INF/flows/")
.addFlowLocation("signup.xml", UrlMap.SIGNUP_WEBFLOW)
.addFlowLocation("user-edit.xml", UrlMap.PROFILE_EDIT_WEBFLOW)
.build();
}
@Bean
public FlowExecutor flowExecutor() {
return getFlowExecutorBuilder(flowRegistry()).build();
}
@Bean
public FlowHandlerAdapter flowHandlerAdapter() {
final FlowHandlerAdapter flowHandlerAdapter = new FlowHandlerAdapter();
flowHandlerAdapter.setFlowExecutor(flowExecutor());
return flowHandlerAdapter;
}
@Bean
public FlowHandlerMapping flowHandlerMapping() {
final FlowHandlerMapping flowHandlerMapping = new FlowHandlerMapping();
flowHandlerMapping.setFlowRegistry(flowRegistry());
// this has to be less than -1
flowHandlerMapping.setOrder(-2);
return flowHandlerMapping;
}
@Bean
public MvcViewFactoryCreator mvcViewFactoryCreator() {
final MvcViewFactoryCreator mvcViewFactoryCreator = new MvcViewFactoryCreator();
final List<ViewResolver> viewResolvers = Collections.singletonList(viewResolver);
mvcViewFactoryCreator.setViewResolvers(viewResolvers);
return mvcViewFactoryCreator;
}
@Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder().setViewFactoryCreator(mvcViewFactoryCreator())
.setValidator(localValidatorFactoryBean()).build();
}
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
}
在Tomcat的context.xml
里面我已经添加了allowCasualMultipartParsing="true"
调试应用程序我可以看到请求中的文件数据,如果我尝试 post 将表单发送到普通控制器,我可以得到它。
我也尝试删除 Spring 安全性,但它在 Spring WebFlow 中仍然不起作用。
在 requestParameters 对象中只有 3 个对象:
- 执行
- _eventid_forward
- _csrf
日志中有一些相关行
DEBUG 2015-03-13 18:03:15,053: org.springframework.web.multipart.support.MultipartFilter - Using MultipartResolver 'filterMultipartResolver' for MultipartFilter
DEBUG 2015-03-13 18:03:15,053: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'filterMultipartResolver'
DEBUG 2015-03-13 18:03:15,053: org.springframework.web.multipart.support.MultipartFilter - Resolving multipart request [/registrazione] with MultipartFilter
DEBUG 2015-03-13 18:03:15,060: org.springframework.web.multipart.commons.CommonsMultipartResolver - Found multipart file [multipartFileUpload] of size 469217 bytes with original filename [PoliziaMunicipale.png], stored in memory
....
DEBUG 2015-03-13 18:03:15,072: org.springframework.binding.mapping.impl.DefaultMapper - Beginning mapping between source [org.springframework.webflow.core.collection.LocalParameterMap] and target [it.openex.pmcommonw.form.FileForm]
DEBUG 2015-03-13 18:03:15,072: org.springframework.binding.mapping.impl.DefaultMapping - Adding mapping result [TargetAccessError@34bc31ea mapping = parameter:'execution' -> execution, code = 'propertyNotFound', error = true, errorCause = org.springframework.binding.expression.PropertyNotFoundException: Property not found, originalValue = 'e1s2', mappedValue = [null]]
DEBUG 2015-03-13 18:03:15,072: org.springframework.binding.mapping.impl.DefaultMapper - Completing mapping between source [org.springframework.webflow.core.collection.LocalParameterMap] and target [it.openex.pmcommonw.form.FileForm]; total mappings = 1; total errors = 1
multipartFileUpload
属性 未绑定在 FileForm
bean 中。
我不确定它是否有用,但在第 52 行 org.springframework.webflow.context.servlet.HttpServletRequestParameterMap
中
if (request instanceof MultipartHttpServletRequest) {
// ... process multipart data
}
检查失败,因为请求是 org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper
的一个实例
更新 1
我可以确认 multipartRequest.getFile("file") 也有效。
虽然我无法启用 org.springframework.web.multipart.support.MultipartFilter
过滤器。
如果启用,multipartRequest 是 StandardMultipartHttpServletRequest
的一个实例,包含一个 Servlet3SecurityContextHolderAwareRequestWrapper
,包装一个 Servlet3SaveToSessionRequestWrapper
,最后包含一个无法访问的 DefaultMultipartHttpServletRequest
和我需要的 multipartFile,但是我看不到。
禁用它我能够得到它,因为 multipartRequest 成为 DefaultMultipartHttpServletRequest
的一个实例,但是没有文件验证并且不遵守 CommonsMultipartResolver
的 maxUploadSize 限制。
此外,如果 Tomcat 启动异常,因为文件对于 Tomcat 的 maxPostSize 限制来说太大了,异常会被我的 CustomAccessDeniedHandler
捕获,因为它的类型是 org.springframework.security.access.AccessDeniedException
,错误信息为Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
.
查看请求对象,我可以看到原始 Tomcat 异常 org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException
。似乎没有什么可以妥善处理的,但是,正如我所说,如果我启用了 MultipartFilter,我将无法获取文件。
我们 运行 遇到了同样的问题,因为我们在我们的 Web 应用程序中使用了 Spring 安全性 4.xx。
问题是 org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper
不是 org.springframework.web.multipart.MultipartHttpServletRequest
的实例,但它包含一个。强制转换为无效,将发生 ClassCastException
。
这就是为什么
if (request instanceof MultipartHttpServletRequest) {
// ... process multipart data
}
永远不会true
.
我们的想法是从原生 HttpServletRequest
中创建一个 org.springframework.web.multipart.support.StandardMultipartHttpServletRequest
,并且成功了。
在我们的 WebApp 中,我们使用 Spring Webflow documentation Section 6.5.1. Invoking a POJO action 中指示的 Pojo 操作。
我们的解决方法:
PojoAction.java
public String fileUpload(RequestContext requestContext) {
final ServletExternalContext context = (ServletExternalContext) requestContext.getExternalContext();
final MultipartHttpServletRequest multipartRequest = new StandardMultipartHttpServletRequest((HttpServletRequest)context.getNativeRequest());
final File file = multipartRequest.getFile("file");
fileUploadHandler.processFile(file); //do something with the submitted file
}
在flow.xml中我们有这样一个动作状态:
<action-state id="upload-action">
<evaluate expression="pojoAction.uploadFile(flowRequestContext)"/>
<transition to="show"/>
</action-state>
在这种情况下,不需要绑定到模型。
希望对您有所帮助!
根据更新 1
在 web.xml 中,CSRF 保护过滤器必须在 SpringSecurityFilterChain 之前声明。
在我们的应用程序中 web.xml 看起来像这样
<filter>
<filter-name>csrfFilter</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>csrfFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
我正在使用 Spring Framework 4.1.5、Spring Security 4.0.0.RC2、Spring Webflow 2.4。0.RELEASE 和 Tomcat 8.0.15.
我遵循了 webflow documentation 中的示例,但我无法在我的表单 bean 中获取文件。
形式
<form:form action="${flowExecutionUrl}" method="post" commandName="fileForm" enctype="multipart/form-data">
<form:input type="file" value="" path="multipartFileUpload"/>
<button type="submit" name="_eventId_forward"><spring:message code="signup.forward"/></button>
<sec:csrfInput/>
</form:form>
表单bean
public class FileForm implements Serializable {
private static final long serialVersionUID = 1L;
private transient MultipartFile multipartFileUpload;
public MultipartFile getMultipartFileUpload() {
return multipartFileUpload;
}
public void setMultipartFileUpload(final MultipartFile multipartFileUpload) {
this.multipartFileUpload = multipartFileUpload;
}
}
流量
<view-state id="companyLogo" view="signup/company-logo" model="fileForm">
<var name="fileForm" class="it.openex.pmcommonw.form.FileForm"/>
<transition on="back" to="chooseProfile" bind="false" validate="false"/>
<transition on="forward" to="companyInfo">
<evaluate expression="userCommonBean.uploadImage(fileForm)"/>
</transition>
</view-state>
支持对象
@Component
public class UserCommonBean {
public static void uploadImage(final FileForm fileForm) throws IOException, ServletException {
fileForm.getMultipartFileUpload(); // always null!!!
}
}
multipartResolver
@Bean
public CommonsMultipartResolver filterMultipartResolver() {
final CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(10 * 1024 * 1024);
multipartResolver.setMaxInMemorySize(1048576);
multipartResolver.setDefaultEncoding("UTF-8");
return multipartResolver;
}
webflow 配置
@Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {
@Autowired
TilesViewResolver viewResolver;
@Bean
public FlowDefinitionRegistry flowRegistry() {
return getFlowDefinitionRegistryBuilder()
.setFlowBuilderServices(flowBuilderServices())
.setBasePath("/WEB-INF/flows/")
.addFlowLocation("signup.xml", UrlMap.SIGNUP_WEBFLOW)
.addFlowLocation("user-edit.xml", UrlMap.PROFILE_EDIT_WEBFLOW)
.build();
}
@Bean
public FlowExecutor flowExecutor() {
return getFlowExecutorBuilder(flowRegistry()).build();
}
@Bean
public FlowHandlerAdapter flowHandlerAdapter() {
final FlowHandlerAdapter flowHandlerAdapter = new FlowHandlerAdapter();
flowHandlerAdapter.setFlowExecutor(flowExecutor());
return flowHandlerAdapter;
}
@Bean
public FlowHandlerMapping flowHandlerMapping() {
final FlowHandlerMapping flowHandlerMapping = new FlowHandlerMapping();
flowHandlerMapping.setFlowRegistry(flowRegistry());
// this has to be less than -1
flowHandlerMapping.setOrder(-2);
return flowHandlerMapping;
}
@Bean
public MvcViewFactoryCreator mvcViewFactoryCreator() {
final MvcViewFactoryCreator mvcViewFactoryCreator = new MvcViewFactoryCreator();
final List<ViewResolver> viewResolvers = Collections.singletonList(viewResolver);
mvcViewFactoryCreator.setViewResolvers(viewResolvers);
return mvcViewFactoryCreator;
}
@Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder().setViewFactoryCreator(mvcViewFactoryCreator())
.setValidator(localValidatorFactoryBean()).build();
}
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
}
在Tomcat的context.xml
里面我已经添加了allowCasualMultipartParsing="true"
调试应用程序我可以看到请求中的文件数据,如果我尝试 post 将表单发送到普通控制器,我可以得到它。
我也尝试删除 Spring 安全性,但它在 Spring WebFlow 中仍然不起作用。
在 requestParameters 对象中只有 3 个对象:
- 执行
- _eventid_forward
- _csrf
日志中有一些相关行
DEBUG 2015-03-13 18:03:15,053: org.springframework.web.multipart.support.MultipartFilter - Using MultipartResolver 'filterMultipartResolver' for MultipartFilter
DEBUG 2015-03-13 18:03:15,053: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'filterMultipartResolver'
DEBUG 2015-03-13 18:03:15,053: org.springframework.web.multipart.support.MultipartFilter - Resolving multipart request [/registrazione] with MultipartFilter
DEBUG 2015-03-13 18:03:15,060: org.springframework.web.multipart.commons.CommonsMultipartResolver - Found multipart file [multipartFileUpload] of size 469217 bytes with original filename [PoliziaMunicipale.png], stored in memory
....
DEBUG 2015-03-13 18:03:15,072: org.springframework.binding.mapping.impl.DefaultMapper - Beginning mapping between source [org.springframework.webflow.core.collection.LocalParameterMap] and target [it.openex.pmcommonw.form.FileForm]
DEBUG 2015-03-13 18:03:15,072: org.springframework.binding.mapping.impl.DefaultMapping - Adding mapping result [TargetAccessError@34bc31ea mapping = parameter:'execution' -> execution, code = 'propertyNotFound', error = true, errorCause = org.springframework.binding.expression.PropertyNotFoundException: Property not found, originalValue = 'e1s2', mappedValue = [null]]
DEBUG 2015-03-13 18:03:15,072: org.springframework.binding.mapping.impl.DefaultMapper - Completing mapping between source [org.springframework.webflow.core.collection.LocalParameterMap] and target [it.openex.pmcommonw.form.FileForm]; total mappings = 1; total errors = 1
multipartFileUpload
属性 未绑定在 FileForm
bean 中。
我不确定它是否有用,但在第 52 行 org.springframework.webflow.context.servlet.HttpServletRequestParameterMap
中
if (request instanceof MultipartHttpServletRequest) {
// ... process multipart data
}
检查失败,因为请求是 org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper
更新 1
我可以确认 multipartRequest.getFile("file") 也有效。
虽然我无法启用 org.springframework.web.multipart.support.MultipartFilter
过滤器。
如果启用,multipartRequest 是 StandardMultipartHttpServletRequest
的一个实例,包含一个 Servlet3SecurityContextHolderAwareRequestWrapper
,包装一个 Servlet3SaveToSessionRequestWrapper
,最后包含一个无法访问的 DefaultMultipartHttpServletRequest
和我需要的 multipartFile,但是我看不到。
禁用它我能够得到它,因为 multipartRequest 成为 DefaultMultipartHttpServletRequest
的一个实例,但是没有文件验证并且不遵守 CommonsMultipartResolver
的 maxUploadSize 限制。
此外,如果 Tomcat 启动异常,因为文件对于 Tomcat 的 maxPostSize 限制来说太大了,异常会被我的 CustomAccessDeniedHandler
捕获,因为它的类型是 org.springframework.security.access.AccessDeniedException
,错误信息为Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
.
查看请求对象,我可以看到原始 Tomcat 异常 org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException
。似乎没有什么可以妥善处理的,但是,正如我所说,如果我启用了 MultipartFilter,我将无法获取文件。
我们 运行 遇到了同样的问题,因为我们在我们的 Web 应用程序中使用了 Spring 安全性 4.xx。
问题是 org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper
不是 org.springframework.web.multipart.MultipartHttpServletRequest
的实例,但它包含一个。强制转换为无效,将发生 ClassCastException
。
这就是为什么
if (request instanceof MultipartHttpServletRequest) {
// ... process multipart data
}
永远不会true
.
我们的想法是从原生 HttpServletRequest
中创建一个 org.springframework.web.multipart.support.StandardMultipartHttpServletRequest
,并且成功了。
在我们的 WebApp 中,我们使用 Spring Webflow documentation Section 6.5.1. Invoking a POJO action 中指示的 Pojo 操作。
我们的解决方法:
PojoAction.java
public String fileUpload(RequestContext requestContext) {
final ServletExternalContext context = (ServletExternalContext) requestContext.getExternalContext();
final MultipartHttpServletRequest multipartRequest = new StandardMultipartHttpServletRequest((HttpServletRequest)context.getNativeRequest());
final File file = multipartRequest.getFile("file");
fileUploadHandler.processFile(file); //do something with the submitted file
}
在flow.xml中我们有这样一个动作状态:
<action-state id="upload-action">
<evaluate expression="pojoAction.uploadFile(flowRequestContext)"/>
<transition to="show"/>
</action-state>
在这种情况下,不需要绑定到模型。 希望对您有所帮助!
根据更新 1
在 web.xml 中,CSRF 保护过滤器必须在 SpringSecurityFilterChain 之前声明。
在我们的应用程序中 web.xml 看起来像这样
<filter>
<filter-name>csrfFilter</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>csrfFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>