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

查看:236
本文介绍了使用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.

I遵循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.

我不确定它是否有用,但在<$ c $里面c> 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.

I无法启用 org.springframework.web.multipart.support.MultipartFilter 过滤器。

如果已启用multipartRequest是 StandardMultipartHttpSe的一个实例rvletRequest 包含 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 ,错误消息是在请求参数'上找到了无效的CSRF令牌'null' _csrf'或标题'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

想法是创建 org.springframework.web.multipart.support来自本地 HttpServletRequest 的.StandardMultipartHttpServletRequest ,它可以工作。

The idea was to create a org.springframework.web.multipart.support.StandardMultipartHttpServletRequest from the native HttpServletRequest and it works.

在我们的WebApp中,我们使用Pojo操作在 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天全站免登陆