jdaugherty commented on code in PR #13863:
URL: https://github.com/apache/grails-core/pull/13863#discussion_r3552114469


##########
grails-doc/src/en/guide/i18n/changingLocales.adoc:
##########
@@ -28,7 +28,19 @@ Grails will automatically switch the user's locale and 
subsequent requests will
 
 By default, Grails uses 
{springapi}org/springframework/web/servlet/i18n/SessionLocaleResolver.html[SessionLocaleResolver]
 as the `localeResolver` bean.
 
-You can change the default locale easily: 
+You can select a different resolver strategy with the 
`grails.i18n.localeResolver` configuration property, without declaring a bean:
+
+[source,yaml]
+.grails-app/conf/application.yml
+----
+grails:
+    i18n:
+        localeResolver: acceptHeader # session (default), cookie, acceptHeader 
or fixed
+----
+
+`session` and `cookie` are mutable, so the `?lang=` switch works with them. 
`acceptHeader` (the locale follows the incoming `Accept-Language` header) and 
`fixed` are read-only, so the `?lang=` switch has no effect and is silently 
ignored. The `fixed` resolver uses `grails.i18n.default.locale`, falling back 
to the JVM default locale.

Review Comment:
   So the url will override the header now with your changes? or vice versa?



##########
grails-core/src/main/groovy/grails/config/Settings.groovy:
##########
@@ -270,6 +270,13 @@ interface Settings {
      */
     String WEB_REMOVE_DEFAULT_VIEW_RESOLVER_BEAN = 
'grails.web.removeDefaultViewResolverBean'
 
+    /**
+     * Whether to remove Spring Boot's welcome-page handler mappings so 
Grails' own URL mappings
+     * own the root path ('/') rather than a static {@code index.html} being 
served for it.
+     * Defaults to true
+     */
+    String WEB_REMOVE_WELCOME_PAGE_MAPPING = 
'grails.web.removeWelcomePageMapping'

Review Comment:
   the spring-config-metadata json file needs updated for this new setting & 
any others that were added.



##########
grails-test-examples/enable-mvc-check/README.md:
##########
@@ -0,0 +1,62 @@
+<!--
+SPDX-License-Identifier: Apache-2.0
+
+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.
+-->
+
+# enable-mvc-check
+
+A dedicated Grails functional test application that pins down the behavior of

Review Comment:
   We should probably remove this file now. It was just here to help you 
understand the test.



##########
grails-web-common/src/test/groovy/grails/web/servlet/mvc/GrailsParameterMapTests.groovy:
##########
@@ -92,82 +97,47 @@ class GrailsParameterMapTests {
     }
 
     @Test
-    void testParseRequestBodyForPutRequest() {
-        def request = new MockHttpServletRequest()
-        request.content = 'foo=bar&one=two'.bytes
-        request.method = 'PUT'
-        request.contentType = "application/x-www-form-urlencoded"
-
-        def params = new GrailsParameterMap(request)
+    void testFormEncodedPutBodyIsExposedAsParams() {
+        def params = new GrailsParameterMap(formFilteredRequest('PUT', 
'foo=bar&one=two', 'application/x-www-form-urlencoded'))
 
         assert 'bar' == params.foo
         assert 'two' == params.one
-
-        params = new GrailsParameterMap(request)
-        assert params.foo == null // should be null, request can't be parsed 
twice
-
-        request = new MockHttpServletRequest()
-        request.method = 'PUT'
-        request.content = 'foo='.bytes
-        request.contentType = "application/x-www-form-urlencoded"
-        request.removeAttribute(GrailsParameterMap.REQUEST_BODY_PARSED)
-
-        params = new GrailsParameterMap(request)
-
-        assert '' == params.foo
     }
 
     @Test
-    void testParseRequestBodyForPutRequestWithCharset() {
-        def request = new MockHttpServletRequest()
-        request.content = 'foo=bar&one=two'.bytes
-        request.method = 'PUT'
-        request.contentType = "application/x-www-form-urlencoded; 
charset=UTF-8"
-
-        def params = new GrailsParameterMap(request)
+    void testFormEncodedPutBodyWithCharsetIsExposedAsParams() {
+        def params = new GrailsParameterMap(formFilteredRequest('PUT', 
'foo=bar&one=two', 'application/x-www-form-urlencoded; charset=UTF-8'))
 
         assert 'bar' == params.foo
         assert 'two' == params.one
-
-        params = new GrailsParameterMap(request)
-        assert params.foo == null // should be null, request can't be parsed 
twice
-
-        request = new MockHttpServletRequest()
-        request.method = 'PUT'
-        request.contentType = "application/x-www-form-urlencoded; 
charset=UTF-8"
-        request.content = 'foo='.bytes
-        request.removeAttribute(GrailsParameterMap.REQUEST_BODY_PARSED)
-
-        params = new GrailsParameterMap(request)
-
-        assert '' == params.foo
     }
 
     @Test
-    void testParseRequestBodyForPatchRequest() {
-        def request = new MockHttpServletRequest()
-        request.content = 'foo=bar&one=two'.bytes
-        request.method = 'PATCH'
-        request.contentType = "application/x-www-form-urlencoded"
-
-        def params = new GrailsParameterMap(request)
+    void testFormEncodedPatchBodyIsExposedAsParams() {
+        def params = new GrailsParameterMap(formFilteredRequest('PATCH', 
'foo=bar&one=two', 'application/x-www-form-urlencoded'))
 
         assert 'bar' == params.foo
         assert 'two' == params.one
+    }
 
-        params = new GrailsParameterMap(request)
-        assert params.foo == null // should be null, request can't be parsed 
twice
-
-        request = new MockHttpServletRequest()

Review Comment:
   Since you're relying on the form filter now, you should add a functioanl 
test to cover these removed scenarios



##########
grails-doc/src/en/guide/upgrading/upgrading80x.adoc:
##########
@@ -1396,6 +1396,31 @@ Grails 8 registers its request-binding filter as a 
`RequestContextFilter` bean s
 This is handled internally and requires no configuration changes.
 Applications that defined their own `GrailsWebRequestFilter` bean, or their 
own `grailsWebRequestFilter` filter-registration bean, continue to override the 
Grails-provided ones.
 
+===== 31.3 Other Boot MVC features now active
+
+Because Boot's `WebMvcAutoConfiguration` is now active, a handful of its 
features that `@EnableWebMvc` previously suppressed take effect for Grails 
servlet web applications.
+None require action for a typical application, but they are behavioral 
differences worth knowing about when you upgrade:
+
+* **Form-content filter for `PUT` / `PATCH` / `DELETE`.** A 
`FormContentFilter` parses `application/x-www-form-urlencoded` bodies of `PUT`, 
`PATCH` and `DELETE` requests so their fields are visible through the standard 
`request.getParameter(...)` API (and therefore in `params`). Boot's 
`OrderedFormContentFilter` provides it for a default application; for an 
application that declares `@EnableWebMvc` — where Boot's MVC auto-configuration 
backs off — Grails contributes an equivalent filter itself, so form parameters 
behave the same either way. In Grails 7 only `PUT` and `PATCH` bodies were 
parsed (by `GrailsParameterMap`) and `DELETE` was not; all three are now 
handled uniformly by the filter. It is enabled by default via 
`spring.mvc.formcontent.filter.enabled`; setting that to `false` disables `PUT` 
/ `PATCH` / `DELETE` form-parameter parsing entirely. An application that reads 
those bodies itself can disable it:
++
+[source,yaml]
+.application.yml
+----
+spring:
+    mvc:
+        formcontent:
+            filter:
+                enabled: false
+----
+
+* **`spring.mvc.*` and `spring.web.*` properties now apply.** Properties such 
as `spring.web.locale`, `spring.mvc.format.date` / `time` / `date-time`, and 
`spring.web.resources.*` were inert before and now take effect. Review any of 
these you may have set inadvertently (for example copied from Boot 
documentation).
+
+* **Boot static-resource handling and welcome page.** Boot adds a catch-all 
resource handler (`classpath:/META-INF/resources/`, `/resources/`, `/static/`, 
`/public/`) and a `WelcomePageHandlerMapping` for a static `index.html`, 
alongside Grails' own resource handling. A request that previously fell through 
to Grails' URL-mapping error handling may now be served as a static resource or 
welcome page — worth checking in applications with catch-all URL mappings or 
custom 404 handling. Set `spring.web.resources.add-mappings: false` to disable 
the static-resource handler.

Review Comment:
   This file is out of date. 



##########
grails-controllers/src/main/groovy/org/grails/plugins/web/controllers/GrailsFormContentFilterAutoConfiguration.java:
##########
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.grails.plugins.web.controllers;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import 
org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
+import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import 
org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.servlet.filter.OrderedFormContentFilter;
+import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.filter.FormContentFilter;
+
+/**
+ * Guarantees a {@link FormContentFilter} for every Grails servlet web 
application, so form-encoded
+ * {@code PUT}, {@code PATCH} and {@code DELETE} bodies are parsed into 
request parameters and are

Review Comment:
   The part I did not understand was this is supposed to only apply for 
`application/x-www-form-urlencoded` request bodies, so I am ok with the 
changes.  But we should update the documentation so this is clear.



##########
grails-web-common/src/test/groovy/grails/web/servlet/mvc/GrailsParameterMapTests.groovy:
##########
@@ -92,82 +97,47 @@ class GrailsParameterMapTests {
     }
 
     @Test
-    void testParseRequestBodyForPutRequest() {
-        def request = new MockHttpServletRequest()
-        request.content = 'foo=bar&one=two'.bytes
-        request.method = 'PUT'
-        request.contentType = "application/x-www-form-urlencoded"
-
-        def params = new GrailsParameterMap(request)
+    void testFormEncodedPutBodyIsExposedAsParams() {
+        def params = new GrailsParameterMap(formFilteredRequest('PUT', 
'foo=bar&one=two', 'application/x-www-form-urlencoded'))
 
         assert 'bar' == params.foo
         assert 'two' == params.one
-
-        params = new GrailsParameterMap(request)
-        assert params.foo == null // should be null, request can't be parsed 
twice
-
-        request = new MockHttpServletRequest()
-        request.method = 'PUT'
-        request.content = 'foo='.bytes
-        request.contentType = "application/x-www-form-urlencoded"
-        request.removeAttribute(GrailsParameterMap.REQUEST_BODY_PARSED)
-
-        params = new GrailsParameterMap(request)
-
-        assert '' == params.foo
     }
 
     @Test
-    void testParseRequestBodyForPutRequestWithCharset() {
-        def request = new MockHttpServletRequest()
-        request.content = 'foo=bar&one=two'.bytes
-        request.method = 'PUT'
-        request.contentType = "application/x-www-form-urlencoded; 
charset=UTF-8"
-
-        def params = new GrailsParameterMap(request)
+    void testFormEncodedPutBodyWithCharsetIsExposedAsParams() {
+        def params = new GrailsParameterMap(formFilteredRequest('PUT', 
'foo=bar&one=two', 'application/x-www-form-urlencoded; charset=UTF-8'))
 
         assert 'bar' == params.foo
         assert 'two' == params.one
-
-        params = new GrailsParameterMap(request)
-        assert params.foo == null // should be null, request can't be parsed 
twice
-
-        request = new MockHttpServletRequest()
-        request.method = 'PUT'
-        request.contentType = "application/x-www-form-urlencoded; 
charset=UTF-8"
-        request.content = 'foo='.bytes
-        request.removeAttribute(GrailsParameterMap.REQUEST_BODY_PARSED)

Review Comment:
   Since you're relying on the form filter now, you should add a functioanl 
test to cover these removed scenarios



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to