Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MultipartConfig with Servlet 3.0 on Spring MVC

How do I add in multipart configuration to a spring mvc app which uses controllers with methods annotated with RequestMapping?

Background:

I want to enable csrf protection and so have added the security:csrf tag in my spring config. I have a controller class with a method annotated with RequestMapping used for uploading files. I also followed the caveat instructions around multipart whereby I added the multipart filter above the security filter. When I tried to upload a file after adding the csrf tag I got an exception around a missing getParts() method. A quick google highlighted this was due to using a version of jetty which was based on the servlet 2.5 spec. I upgraded jetty-maven-plugin to 8.1.14.v20131031 and tried uploading again. Resulting in:

org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: No multipart config for servlet
        at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:68)
        at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:58)
        at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:110)

Where do I put the multipart configuration for xml setup? All documentation says to add the multipart-config in the servlet tag for the specific servlet in web.xml. There is only a single servlet for my application though. So I added it to that and still I get the same issue.

<servlet>
    <servlet-name>SpringDispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <multipart-config>
        <location>/tmp</location>
        <max-file-size>20848820</max-file-size>
        <max-request-size>418018841</max-request-size>
        <file-size-threshold>1048576</file-size-threshold>
    </multipart-config>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/servlet-context.xml
        </param-value>
    </init-param>
    <load-on-startup>10</load-on-startup>
</servlet>

I also updated the schema location at the top of web.xml to point at version 3.0 of the servlet spec, sourced from http://www.mkyong.com/web-development/the-web-xml-deployment-descriptor-examples/.

Any help?

Edit: added the following riles for Rob:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/spring/webapp.xml</param-value>
    </context-param>

    <context-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>OracleDB,common</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <listener>
        <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>SpringDispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <multipart-config>
            <location>/tmp</location>
            <max-file-size>20848820</max-file-size>
            <max-request-size>418018841</max-request-size>
            <file-size-threshold>1048576</file-size-threshold>
        </multipart-config>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/servlet-context.xml
            </param-value>
        </init-param>
        <load-on-startup>10</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>SpringDispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>MultipartFilter</filter-name>
        <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>MultipartFilter</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>
    </filter-mapping>

    <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>sitemesh</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>XSS</filter-name>
        <filter-class>com.mycompany.CrossScriptingFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>XSS</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>

    <resource-ref>
        <description>Core Datasource</description>
        <res-ref-name>jdbc/coreDataSource</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

    <resource-ref>
        <description>Location Datasource</description>
        <res-ref-name>jdbc/locationDataSource</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

    <error-page>
        <!-- Missing login -->
        <error-code>401</error-code>
        <location>/WEB-INF/views/errorPage.jsp</location>
    </error-page>
    <error-page>
        <!-- Forbidden directory listing -->
        <error-code>403</error-code>
        <location>/WEB-INF/views/errorPage.jsp</location>
    </error-page>
    <error-page>
        <!-- Missing resource -->
        <error-code>404</error-code>
        <location>/WEB-INF/views/errorPageNotFound.jsp</location>
    </error-page>
    <error-page>
        <!-- Uncaught exception -->
        <error-code>500</error-code>
        <location>/WEB-INF/views/errorPage.jsp</location>
    </error-page>
    <error-page>
        <!-- Unsupported servlet method -->
        <error-code>503</error-code>
        <location>/WEB-INF/views/errorPage.jsp</location>
    </error-page>

</web-app>

servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util" xmlns:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">

    <mvc:annotation-driven />

    <mvc:resources mapping="/resources/**" location="/resources/" />
    <mvc:resources mapping="/images/**" location="file:${fileSystemStore.fileSystemStorageLocation}"/>

    <context:component-scan base-package="com.mycompany.console.*" />

    <mvc:interceptors>
        <bean class="com.mycompany.security.ChangePasswordInterceptor" />
    </mvc:interceptors>

    <security:global-method-security
        secured-annotations="enabled" jsr250-annotations="enabled"
        pre-post-annotations="enabled" proxy-target-class="true" />

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass"
            value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
        <property name="contentType" value="text/html;charset=UTF-8" />
    </bean>

    <bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="100000000"/>
    </bean>

    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>classpath:language</value>
                <value>classpath:language_additions</value>
                <value>classpath:formats</value>
            </list>
        </property>
        <property name="defaultEncoding" value="UTF-8" />
    </bean>

    <bean id="localeResolver" class="com.mycompany.locale.SessionLocaleResolver"/>

</beans>
like image 544
edwardmlyte Avatar asked Apr 22 '14 15:04

edwardmlyte


People also ask

Does Spring MVC use servlets?

Spring's web MVC framework is, like many other web MVC frameworks, request-driven, designed around a central Servlet that dispatches requests to controllers and offers other functionality that facilitates the development of web applications. Spring's DispatcherServlet however, does more than just that.

What is the use of dispatcher servlet in Spring MVC?

The DispatcherServlet is the front controller in Spring web applications. It's used to create web applications and REST services in Spring MVC. In a traditional Spring web application, this servlet is defined in the web. xml file.

What is the use of servlet xml in Spring MVC?

spring-servlet. xml. In Spring, a single front servlet takes the incoming requests and delegates them to appropriate controller methods. The front servlet, based on a Front controller design pattern, handles all the HTTP requests of a particular web application.

Does spring boot use dispatcher servlet?

A typical MVC database driven Spring MVC application requires a lot of configuration such as dispatcher servlet, a view resolver, Jackson, data source, transaction manager, among many others. Spring Boot auto-configures a Dispatcher Servlet if Spring MVC jar is on the classpath.


1 Answers

I believe you are having issues related to SPR-11373. Specifically, the servlet specification is not clear on what should happen when performing multipart resolution within a Filter.

Have you tried using commons-fileupload instead? This is likely your best option. First add the following dependency:

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.2.2</version>
</dependency>

Next ensure you have the following bean definition in your root application context.

<bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000000"/>
</bean>

You can find a complete working example with both commons-upload (prefer this solution) and using tomcat using allowCasualMultipartParsing on SEC-2471

like image 161
Rob Winch Avatar answered Sep 27 '22 21:09

Rob Winch