diff --git a/spring-web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java b/spring-web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java index e2946ab9d53..dc06f0768be 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java @@ -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. @@ -21,6 +21,7 @@ import javax.servlet.http.Part; 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; @@ -32,6 +33,15 @@ import org.springframework.web.multipart.MultipartResolver; * To be added as "multipartResolver" bean to a Spring DispatcherServlet context, * without any extra configuration at the bean level (see below). * + *
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. + * *
Note: 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} @@ -55,6 +65,7 @@ import org.springframework.web.multipart.MultipartResolver; * @author Juergen Hoeller * @since 3.1 * @see #setResolveLazily + * @see #setStrictServletCompliance * @see HttpServletRequest#getParts() * @see org.springframework.web.multipart.commons.CommonsMultipartResolver */ @@ -62,6 +73,8 @@ 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 @@ -76,10 +89,32 @@ public class StandardServletMultipartResolver implements MultipartResolver { this.resolveLazily = resolveLazily; } + /** + * Specify whether this resolver should strictly comply with the Servlet + * specification, only kicking in for "multipart/form-data" requests. + *
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. + *
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 diff --git a/spring-web/src/test/java/org/springframework/web/multipart/support/StandardServletMultipartResolverTests.java b/spring-web/src/test/java/org/springframework/web/multipart/support/StandardServletMultipartResolverTests.java new file mode 100644 index 00000000000..4cd5ae0d9fb --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/multipart/support/StandardServletMultipartResolverTests.java @@ -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(); + } + +} diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index 654a8c63475..01bc7d18329 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -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]] @@ -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]]