This is an automated email from the ASF dual-hosted git repository.

matrei pushed a commit to branch fix/issue-16145
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 41730acd35f44fc088b5cca4701e5dec4b7d1d8a
Author: Mattias Reichel <[email protected]>
AuthorDate: Thu Aug 13 13:36:32 2026 +0200

    fix: issue 16145
    
    Closes gh-16145
---
 .../web/controllers/ControllersAutoConfiguration.java     | 15 +++++++++++++++
 .../org/grails/web/filters/HiddenHttpMethodFilter.java    | 10 +++++++++-
 .../grails/web/servlet/mvc/GrailsDispatcherServlet.groovy | 12 ++++++++++++
 .../grails/web/servlet/mvc/GrailsWebRequestFilter.java    |  4 ++++
 4 files changed, 40 insertions(+), 1 deletion(-)

diff --git 
a/grails-controllers/src/main/groovy/org/grails/plugins/web/controllers/ControllersAutoConfiguration.java
 
b/grails-controllers/src/main/groovy/org/grails/plugins/web/controllers/ControllersAutoConfiguration.java
index e9742d7471..6d1207daa7 100644
--- 
a/grails-controllers/src/main/groovy/org/grails/plugins/web/controllers/ControllersAutoConfiguration.java
+++ 
b/grails-controllers/src/main/groovy/org/grails/plugins/web/controllers/ControllersAutoConfiguration.java
@@ -40,6 +40,7 @@ import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.util.ClassUtils;
 import org.springframework.web.filter.CharacterEncodingFilter;
+import org.springframework.web.multipart.support.MultipartFilter;
 import org.springframework.web.servlet.DispatcherServlet;
 import 
org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -101,6 +102,20 @@ public class ControllersAutoConfiguration {
         return characterEncodingFilter;
     }
 
+    @Bean
+    @ConditionalOnMissingBean(MultipartFilter.class)
+    public FilterRegistrationBean<Filter> multipartFilter() {
+        var registrationBean = new FilterRegistrationBean<>();
+        var multipartFilter = new MultipartFilter();
+        
multipartFilter.setMultipartResolverBeanName(GrailsApplication.MULTIPART_RESOLVER_BEAN);
+        registrationBean.setFilter(multipartFilter);
+        registrationBean.addUrlPatterns(Settings.DEFAULT_WEB_SERVLET_PATH);
+        // Resolve multipart requests before HiddenHttpMethodFilter and 
security filters access parameters.
+        // Otherwise, the servlet container parses the parts first and 
oversized uploads bypass the multipart resolver.
+        registrationBean.setOrder(GrailsFilters.FIRST.getOrder());
+        return registrationBean;
+    }
+
     @Bean
     @ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
     public FilterRegistrationBean<Filter> hiddenHttpMethodFilter() {
diff --git 
a/grails-web-mvc/src/main/groovy/org/grails/web/filters/HiddenHttpMethodFilter.java
 
b/grails-web-mvc/src/main/groovy/org/grails/web/filters/HiddenHttpMethodFilter.java
index 59c8fc3322..7282353607 100644
--- 
a/grails-web-mvc/src/main/groovy/org/grails/web/filters/HiddenHttpMethodFilter.java
+++ 
b/grails-web-mvc/src/main/groovy/org/grails/web/filters/HiddenHttpMethodFilter.java
@@ -71,7 +71,10 @@ public class HiddenHttpMethodFilter extends 
OncePerRequestFilter {
     }
 
     protected String getHttpMethodOverride(HttpServletRequest request) {
-        String httpMethod = request.getParameter(methodParam);
+        String httpMethod = null;
+        if (!isMultipart(request)) {
+            httpMethod = request.getParameter(methodParam);
+        }
 
         if (httpMethod == null) {
             httpMethod = request.getHeader(HEADER_X_HTTP_METHOD_OVERRIDE);
@@ -79,6 +82,11 @@ public class HiddenHttpMethodFilter extends 
OncePerRequestFilter {
         return httpMethod == null ? null : httpMethod.toUpperCase();
     }
 
+    private boolean isMultipart(HttpServletRequest request) {
+        var contentType = request.getContentType();
+        return contentType != null && contentType.regionMatches(true, 0, 
"multipart/", 0, "multipart/".length());
+    }
+
     /**
      * Simple {@link HttpServletRequest} wrapper that returns the supplied 
method for
      * {@link HttpServletRequest#getMethod()}.
diff --git 
a/grails-web-mvc/src/main/groovy/org/grails/web/servlet/mvc/GrailsDispatcherServlet.groovy
 
b/grails-web-mvc/src/main/groovy/org/grails/web/servlet/mvc/GrailsDispatcherServlet.groovy
index 5c5331e1bf..79cf359a3b 100644
--- 
a/grails-web-mvc/src/main/groovy/org/grails/web/servlet/mvc/GrailsDispatcherServlet.groovy
+++ 
b/grails-web-mvc/src/main/groovy/org/grails/web/servlet/mvc/GrailsDispatcherServlet.groovy
@@ -33,6 +33,7 @@ import org.springframework.web.context.WebApplicationContext
 import org.springframework.web.context.request.RequestAttributes
 import org.springframework.web.context.request.ServletRequestAttributes
 import org.springframework.web.multipart.MultipartException
+import org.springframework.web.multipart.MultipartHttpServletRequest
 import org.springframework.web.servlet.DispatcherServlet
 
 import grails.util.Holders
@@ -65,6 +66,7 @@ class GrailsDispatcherServlet extends DispatcherServlet 
implements ServletContex
         else {
             GrailsWebRequest webRequest = (GrailsWebRequest) previousAttributes
             if (webRequest.isActive()) {
+                propagateMultipartRequest(webRequest, request)
                 return webRequest
             }
             else {
@@ -75,10 +77,19 @@ class GrailsDispatcherServlet extends DispatcherServlet 
implements ServletContex
 
     protected GrailsWebRequest buildGrailsWebRequest(HttpServletRequest 
request, HttpServletResponse response) {
         def webRequest = new GrailsWebRequest(request, response, 
request.getServletContext())
+        propagateMultipartRequest(webRequest, request)
         webRequest.informParameterCreationListeners()
         return webRequest
     }
 
+    private void propagateMultipartRequest(GrailsWebRequest webRequest, 
HttpServletRequest request) {
+        def multipartRequest =
+                
org.springframework.web.util.WebUtils.getNativeRequest(request, 
MultipartHttpServletRequest)
+        if (multipartRequest != null) {
+            webRequest.multipartRequest = multipartRequest
+        }
+    }
+
     @Override
     protected HttpServletRequest checkMultipart(HttpServletRequest request) 
throws MultipartException {
         boolean shouldProcessMultiPart = !WebUtils.isError(request) && 
!WebUtils.isForwardOrInclude(request)
@@ -89,6 +100,7 @@ class GrailsDispatcherServlet extends DispatcherServlet 
implements ServletContex
                 if (webRequest != null) {
                     webRequest.multipartRequest = processedRequest
                 }
+                return processedRequest
             }
         }
         return request
diff --git 
a/grails-web-mvc/src/main/groovy/org/grails/web/servlet/mvc/GrailsWebRequestFilter.java
 
b/grails-web-mvc/src/main/groovy/org/grails/web/servlet/mvc/GrailsWebRequestFilter.java
index 820ee1a00b..e86d07d954 100644
--- 
a/grails-web-mvc/src/main/groovy/org/grails/web/servlet/mvc/GrailsWebRequestFilter.java
+++ 
b/grails-web-mvc/src/main/groovy/org/grails/web/servlet/mvc/GrailsWebRequestFilter.java
@@ -31,6 +31,7 @@ import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.i18n.LocaleContextHolder;
 import org.springframework.web.filter.RequestContextFilter;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
 
 import grails.web.mvc.FlashScope;
 import org.grails.web.util.WebUtils;
@@ -58,6 +59,9 @@ public class GrailsWebRequestFilter extends 
RequestContextFilter implements Appl
         boolean isIncludeOrForward = WebUtils.isForwardOrInclude(request);
         GrailsWebRequest previous = isIncludeOrForward ? 
GrailsWebRequest.lookup(request) : null;
         GrailsWebRequest webRequest = new GrailsWebRequest(request, response, 
getServletContext());
+        if (request instanceof MultipartHttpServletRequest) {
+            webRequest.setMultipartRequest(request);
+        }
         configureParameterCreationListeners(webRequest);
 
         if (logger.isDebugEnabled()) {

Reply via email to