使用 Spring WebFlow 2.4.0 上传文件,未绑定参数 [英] File Upload using Spring WebFlow 2.4.0, parameter not binded

查看:22
本文介绍了使用 Spring WebFlow 2.4.0 上传文件,未绑定参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆