Skip to content

Commit

Permalink
StandardServletMultipartResolver provides strict Servlet compliance o…
Browse files Browse the repository at this point in the history
…ption

Closes spring-projectsgh-26826
  • Loading branch information
jhoeller authored and lxbzmy committed Mar 26, 2022
1 parent a0cd66a commit 84daa2d
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 8 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,6 +21,7 @@

import org.apache.commons.logging.LogFactory;

import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartHttpServletRequest;
Expand All @@ -32,6 +33,15 @@
* To be added as "multipartResolver" bean to a Spring DispatcherServlet context,
* without any extra configuration at the bean level (see below).
*
* <p>This resolver variant uses your Servlet container's multipart parser as-is,
* potentially exposing the application to container implementation differences.
* See {@link org.springframework.web.multipart.commons.CommonsMultipartResolver}
* for an alternative implementation using a local Commons FileUpload library
* within the application, providing maximum portability across Servlet containers.
* Also, see this resolver's configuration option for
* {@link #setStrictServletCompliance strict Servlet compliance}, narrowing the
* applicability of Spring's {@link MultipartHttpServletRequest} to form data only.
*
* <p><b>Note:</b> In order to use Servlet 3.0 based multipart parsing,
* you need to mark the affected servlet with a "multipart-config" section in
* {@code web.xml}, or with a {@link javax.servlet.MultipartConfigElement}
Expand All @@ -55,13 +65,16 @@
* @author Juergen Hoeller
* @since 3.1
* @see #setResolveLazily
* @see #setStrictServletCompliance
* @see HttpServletRequest#getParts()
* @see org.springframework.web.multipart.commons.CommonsMultipartResolver
*/
public class StandardServletMultipartResolver implements MultipartResolver {

private boolean resolveLazily = false;

private boolean strictServletCompliance = false;


/**
* Set whether to resolve the multipart request lazily at the time of
Expand All @@ -76,10 +89,32 @@ public void setResolveLazily(boolean resolveLazily) {
this.resolveLazily = resolveLazily;
}

/**
* Specify whether this resolver should strictly comply with the Servlet
* specification, only kicking in for "multipart/form-data" requests.
* <p>Default is "false", trying to process any request with a "multipart/"
* content type as far as the underlying Servlet container supports it
* (which works on e.g. Tomcat but not on Jetty). For consistent portability
* and in particular for consistent custom handling of non-form multipart
* request types outside of Spring's {@link MultipartResolver} mechanism,
* switch this flag to "true": Only "multipart/form-data" requests will be
* wrapped with a {@link MultipartHttpServletRequest} then; other kinds of
* requests will be left as-is, allowing for custom processing in user code.
* <p>Note that Commons FileUpload and therefore
* {@link org.springframework.web.multipart.commons.CommonsMultipartResolver}
* supports any "multipart/" request type. However, it restricts processing
* to POST requests which standard Servlet multipart parsers might not do.
* @since 5.3.9
*/
public void setStrictServletCompliance(boolean strictServletCompliance) {
this.strictServletCompliance = strictServletCompliance;
}


@Override
public boolean isMultipart(HttpServletRequest request) {
return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
return StringUtils.startsWithIgnoreCase(request.getContentType(),
(this.strictServletCompliance ? MediaType.MULTIPART_FORM_DATA_VALUE : "multipart/"));
}

@Override
Expand Down
@@ -0,0 +1,90 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.web.multipart.support;

import org.junit.jupiter.api.Test;

import org.springframework.http.MediaType;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;

import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Juergen Hoeller
*/
public class StandardServletMultipartResolverTests {

@Test
public void isMultipartWithDefaultSetting() {
StandardServletMultipartResolver resolver = new StandardServletMultipartResolver();

MockHttpServletRequest request = new MockHttpServletRequest("POST", "/");
assertThat(resolver.isMultipart(request)).isFalse();

request.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE);
assertThat(resolver.isMultipart(request)).isTrue();

request.setContentType(MediaType.MULTIPART_MIXED_VALUE);
assertThat(resolver.isMultipart(request)).isTrue();

request.setContentType(MediaType.MULTIPART_RELATED_VALUE);
assertThat(resolver.isMultipart(request)).isTrue();

request = new MockHttpServletRequest("PUT", "/");
assertThat(resolver.isMultipart(request)).isFalse();

request.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE);
assertThat(resolver.isMultipart(request)).isTrue();

request.setContentType(MediaType.MULTIPART_MIXED_VALUE);
assertThat(resolver.isMultipart(request)).isTrue();

request.setContentType(MediaType.MULTIPART_RELATED_VALUE);
assertThat(resolver.isMultipart(request)).isTrue();
}

@Test
public void isMultipartWithStrictSetting() {
StandardServletMultipartResolver resolver = new StandardServletMultipartResolver();
resolver.setStrictServletCompliance(true);

MockHttpServletRequest request = new MockHttpServletRequest("POST", "/");
assertThat(resolver.isMultipart(request)).isFalse();

request.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE);
assertThat(resolver.isMultipart(request)).isTrue();

request.setContentType(MediaType.MULTIPART_MIXED_VALUE);
assertThat(resolver.isMultipart(request)).isFalse();

request.setContentType(MediaType.MULTIPART_RELATED_VALUE);
assertThat(resolver.isMultipart(request)).isFalse();

request = new MockHttpServletRequest("PUT", "/");
assertThat(resolver.isMultipart(request)).isFalse();

request.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE);
assertThat(resolver.isMultipart(request)).isTrue();

request.setContentType(MediaType.MULTIPART_MIXED_VALUE);
assertThat(resolver.isMultipart(request)).isFalse();

request.setContentType(MediaType.MULTIPART_RELATED_VALUE);
assertThat(resolver.isMultipart(request)).isFalse();
}

}
34 changes: 28 additions & 6 deletions src/docs/asciidoc/web/webmvc.adoc
Expand Up @@ -1140,18 +1140,30 @@ another based on Servlet 3.0 multipart request parsing.

To enable multipart handling, you need to declare a `MultipartResolver` bean in your
`DispatcherServlet` Spring configuration with a name of `multipartResolver`.
The `DispatcherServlet` detects it and applies it to the incoming request. When a POST with
content-type of `multipart/form-data` is received, the resolver parses the content and
wraps the current `HttpServletRequest` as `MultipartHttpServletRequest` to
provide access to resolved parts in addition to exposing them as request parameters.
The `DispatcherServlet` detects it and applies it to the incoming request. When a POST
with a content type of `multipart/form-data` is received, the resolver parses the
content wraps the current `HttpServletRequest` as a `MultipartHttpServletRequest` to
provide access to resolved files in addition to exposing parts as request parameters.


[[mvc-multipart-resolver-commons]]
==== Apache Commons `FileUpload`

To use Apache Commons `FileUpload`, you can configure a bean of type
`CommonsMultipartResolver` with a name of `multipartResolver`. You also need to
have `commons-fileupload` as a dependency on your classpath.
`CommonsMultipartResolver` with a name of `multipartResolver`. You also need to have
the `commons-fileupload` jar as a dependency on your classpath.

This resolver variant delegates to a local library within the application, providing
maximum portability across Servlet containers. As an alternative, consider standard
Servlet multipart resolution through the container's own parser as discussed below.

[NOTE]
====
Commons FileUpload traditionally applies to POST requests only but accepts any
`multipart/` content type. See the
{api-spring-framework}/web/multipart/commons/CommonsMultipartResolver.html[`CommonsMultipartResolver`]
javadoc for details and configuration options.
====


[[mvc-multipart-resolver-standard]]
Expand Down Expand Up @@ -1200,6 +1212,16 @@ The following example shows how to set a `MultipartConfigElement` on the Servlet
Once the Servlet 3.0 configuration is in place, you can add a bean of type
`StandardServletMultipartResolver` with a name of `multipartResolver`.

[NOTE]
====
This resolver variant uses your Servlet container's multipart parser as-is,
potentially exposing the application to container implementation differences.
By default, it will try to parse any `multipart/` content type with any HTTP
method but this may not be supported across all Servlet containers. See the
{api-spring-framework}/web/multipart/support/StandardServletMultipartResolver.html[`StandardServletMultipartResolver`]
javadoc for details and configuration options.
====



[[mvc-logging]]
Expand Down

0 comments on commit 84daa2d

Please sign in to comment.