codeconsole commented on PR #13863: URL: https://github.com/apache/grails-core/pull/13863#issuecomment-4925568550
## Follow-on changes: neutralizing the Boot MVC defaults exposed by removing `@EnableWebMvc` Removing the auto-injected `@EnableWebMvc` means Spring Boot's `WebMvcAutoConfiguration` is now active (and Spring's `WebMvcConfigurationSupport` no longer owns MVC config). That's the goal — Grails participates in the Boot auto-configuration ecosystem — but it also switches on a handful of Boot / Spring MVC defaults that overlap with, or silently change, long-standing Grails behavior. These commits keep the pieces Grails should own while letting the rest of `WebMvcAutoConfiguration` through. ### 1. Static resources — don't serve `classpath:/public` at the context root `WebMvcAutoConfiguration` registers a catch‑all `/**` resource handler over `classpath:/META-INF/resources/`, `classpath:/resources/`, `classpath:/static/` and `classpath:/public/`. Grails already serves those exact locations under `/static/**` (`ControllersAutoConfiguration.GrailsWebMvcConfigurer`), so Boot's `/**` is both redundant and a behavior change: any unmapped URL can now be shadowed by a file under `classpath:/public` where it previously 404'd. - **`GrailsWebResourcesEnvironmentPostProcessor`** contributes `spring.web.resources.add-mappings=false` as an overridable default, so Boot's catch‑all is off while Grails' own `/static/**` serving (gated by `grails.resources.enabled`) is unaffected. Apps that want Boot's behavior set `spring.web.resources.add-mappings=true`. ### 2. Welcome page — Grails' `UrlMappings` own `/` `WebMvcAutoConfiguration` also registers `welcomePageHandlerMapping` / `welcomePageNotAcceptableHandlerMapping`, which serve a static `index.html` at `/`. That overlaps with Grails' `UrlMappings`-based root handling and can shadow an application's own `"/"` mapping whenever such an `index.html` happens to be on the classpath. - **`GrailsWelcomePageAutoConfiguration`** removes both welcome-page mappings so the root path falls through to `UrlMappings`. Opt out with `grails.web.removeWelcomePageMapping=false`. ### 3. Locale resolution — `?lang=` regressed silently under `@EnableWebMvc` The most subtle one. In **Spring Framework 7, `WebMvcConfigurationSupport` now contributes its own `localeResolver` bean** (an `AcceptHeaderLocaleResolver`). Because `@EnableWebMvc` imports `WebMvcConfigurationSupport`, that bean exists *before* `I18nAutoConfiguration` is evaluated, so grails-i18n's `@ConditionalOnMissingBean(name = "localeResolver")` backed off — Grails' `SessionLocaleResolver` never registered and **`?lang=` locale switching silently stopped working**. Worse, `ParamsAwareLocaleChangeInterceptor` still ran and logged an `UnsupportedOperationException` from `AcceptHeaderLocaleResolver.setLocale` on every `?lang=` request. This is a regression from Grails 7: on 7.2.x `I18nAutoConfiguration.localeResolver()` registered the `SessionLocaleResolver` **unconditionally**, so it always took precedence even with the auto-injected `@EnableWebMvc`. (Confirmed empirically: dropping the `@ConditionalOnMissingBean` restores `?lang=de` → `de`; keeping it yields the `Accept-Language` header value.) Fixes: - **`GrailsLocaleResolverAutoConfiguration`** (ordered before `I18nAutoConfiguration`) removes *only* the `WebMvcConfigurationSupport`-contributed `localeResolver`, identified by its factory-bean type, so grails-i18n's configured resolver registers instead. An application-defined `localeResolver` (from `resources.groovy` or a user `@Configuration`) is left untouched, so an explicit app resolver still wins. - **`grails.i18n.localeResolver` is now configurable** — `session` (default), `cookie`, `acceptHeader`, or `fixed`. Header-based resolution is a clean opt‑in for apps that prefer it; `acceptHeader`/`fixed` are read-only, so `?lang=` simply has no effect with them. - **`ParamsAwareLocaleChangeInterceptor`** now handles a read-only resolver gracefully — it logs the unsupported `setLocale` at debug rather than erroring on every request. The interceptor stays registered (so `urlMappingsHandlerMapping` keeps its required `HandlerInterceptor`); it just no-ops when the resolver can't change. ### Tests & docs - Unit coverage: `GrailsWelcomePageAutoConfigurationSpec`, `GrailsWebResourcesEnvironmentPostProcessorSpec`, `GrailsLocaleResolverAutoConfigurationSpec`, updated `I18nAutoConfigurationSpec`, and a read-only-resolver case in `ParamsAwareLocaleChangeInterceptorTests`. - The `enable-mvc-check` integration app exercises all of the above end-to-end (`classpath:/public` 404, welcome-page 404, form-content parsing, and — via `grails.i18n.localeResolver=acceptHeader` — header-based locale resolution with no error spam). - `changingLocales.adoc` documents the new `grails.i18n.localeResolver` property. -- 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]
