使用 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.
I'm using Spring Framework 4.1.5, Spring Security 4.0.0.RC2, Spring Webflow 2.4.0.RELEASE and Tomcat 8.0.15.
我遵循了 webflow 文档中的示例,但我无法在我的表单 bean 中获取该文件.
I followed the example in the webflow documentation, but I can't get the file in my form 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
The 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"
Inside Tomcat's context.xml
I already added allowCasualMultipartParsing="true"
调试应用程序我可以看到请求中的文件数据,如果我尝试将表单发布到普通控制器,我可以得到它.
Debugging the application I can see the file data inside the request, and I can get it if I try to post the form to a normal controller.
我也尝试删除 Spring Security,但它仍然无法在 Spring WebFlow 中工作.
I tried also to remove Spring Security but it still didn't work inside Spring WebFlow.
在 requestParameters 对象中只有 3 个对象:
In the requestParameters object there are only 3 objects:
- 执行
- _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 中.
The multipartFileUpload
property is not binded in the FileForm
bean.
我不确定它是否有用,但在 org.springframework.webflow.context.servlet.HttpServletRequestParameterMap
中的第 52 行
I'm not sure if it's useful, but inside org.springframework.webflow.context.servlet.HttpServletRequestParameterMap
at line 52
if (request instanceof MultipartHttpServletRequest) {
// ... process multipart data
}
检查失败,因为请求是 org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper
it fails the check because the request is an instance of org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper
我可以确认 multipartRequest.getFile("file") 也有效.
I can confirm that multipartRequest.getFile("file") also works.
我无法启用 org.springframework.web.multipart.support.MultipartFilter
过滤器.
I can't enable the org.springframework.web.multipart.support.MultipartFilter
filter though.
如果启用了 multipartRequest 是一个 StandardMultipartHttpServletRequest
的实例,它包含一个 Servlet3SecurityContextHolderAwareRequestWrapper
,包装一个 Servlet3SaveToSessionRequestWrapper
,最后包含一个无法访问的 DefaultMultipartHttpServletRequest
带有我需要的 multipartFile,但我无法得到它.
If it's enabled the multipartRequest is an instance of StandardMultipartHttpServletRequest
containing a Servlet3SecurityContextHolderAwareRequestWrapper
, wrapping a Servlet3SaveToSessionRequestWrapper
, finally containing an unreachable DefaultMultipartHttpServletRequest
with the multipartFile I need, but I can't get it.
禁用它我能够得到它,因为 multipartRequest 成为了 DefaultMultipartHttpServletRequest
的一个实例,但是没有文件验证并且 CommonsMultipartResolver
的 maxUploadSize 限制没有得到遵守.
Disabling it I'm able to get it because multipartRequest became an instance of DefaultMultipartHttpServletRequest
, but there's no file validation and the maxUploadSize limit of CommonsMultipartResolver
is not respected.
另外,如果 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'.
.
Plus if Tomcat launches an exception because the file is too big for Tomcat's maxPostSize limit, the exception is caught by my CustomAccessDeniedHandler
because its type is org.springframework.security.access.AccessDeniedException
, and the error message is 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,我将无法获取文件.
Looking at the request object I can see the original Tomcat exception org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException
. It seems like there's nothing to handle it properly, but, as I said, if I enable the MultipartFilter I can't get the file.
推荐答案
我们遇到了同样的问题,因为我们在 Web 应用程序中使用 Spring Security 4.xx.问题是 org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper
不是 org.springframework.web.multipart.MultipartHttpServletRequest
的实例,但它包含一个.强制转换将不起作用,并且会发生 ClassCastException
.
We ran into the same problems, since we use Spring Security 4.xx in our web application.
The Problem is that a org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper
isn't instance of org.springframework.web.multipart.MultipartHttpServletRequest
but it contains one. A cast to won't work and ClassCastException
will occur.
这就是为什么
if (request instanceof MultipartHttpServletRequest) {
// ... process multipart data
}
永远不可能true
.
这个想法是从原生 HttpServletRequest
创建一个 org.springframework.web.multipart.support.StandardMultipartHttpServletRequest
并且它有效.
The idea was to create a org.springframework.web.multipart.support.StandardMultipartHttpServletRequest
from the native HttpServletRequest
and it works.
在我们的 WebApp 中,我们使用 Spring Webflow 文档第 6.5.1 节.调用 POJO 操作.
In our WebApp we use Pojo Actions indicated in Spring Webflow documentation Section 6.5.1. Invoking a POJO action.
我们的解决方法:
PojoAction.java
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 中,我们有一个这样的动作状态:
In flow.xml we have an action state like this:
<action-state id="upload-action">
<evaluate expression="pojoAction.uploadFile(flowRequestContext)"/>
<transition to="show"/>
</action-state>
在这种情况下,不需要绑定到模型.希望能帮到你!
In this case the binding to a model is not needed. I hope it helps!
根据更新 1
在 web.xml 中,CSRF 保护过滤器必须在 SpringSecurityFilterChain 之前声明.
In web.xml the CSRF-Protection Filter must declared before SpringSecurityFilterChain.
在我们的应用程序中,web.xml 看起来像这样
In our application the web.xml looks like this
<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 WebFlow 2.4.0 上传文件,未绑定参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!