codeconsole commented on code in PR #16237:
URL: https://github.com/apache/grails-core/pull/16237#discussion_r4213523592


##########
grails-converters/src/main/groovy/grails/converters/JSON.java:
##########
@@ -423,14 +439,32 @@ public static void use(String cfgName) throws 
ConverterException {
         }
     }
 
+    /**
+     * @deprecated Prefer a Spring Boot {@code JsonMapperBuilderCustomizer} 
that registers a Jackson
+     * {@code SimpleModule} or {@code ValueSerializer}. A registered 
marshaller applies only to the
+     * legacy converter, and keeps {@code respond} on it unless {@code 
grails.web.rendering.json.spring} is set.
+     */
+    @Deprecated(since = "9.0", forRemoval = true)

Review Comment:
   - The remaining `forRemoval` deprecations (`createNamedConfig`, both `use` 
overloads, `getNamedConfig`, `withDefaultConfiguration`) say "Scheduled for 
removal in Grails 11", as does the guide.
   - `registerObjectMarshaller` is no longer deprecated (d81e3956db). It stays 
the supported way to customize `render ... as JSON` and the legacy `respond()` 
path, as in #16414, and registering a marshaller no longer changes which path 
`respond()` takes.



##########
grails-doc/src/en/guide/upgrading/upgrading90x.adoc:
##########
@@ -0,0 +1,289 @@
+////
+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.
+////
+
+=== Upgrade Instructions for Grails and Related Dependencies
+
+This guide outlines the changes to review when you upgrade a Grails project 
from Grails 8 to Grails 9.
+
+==== 1. XML Web Support Is Optional
+
+XML conversion, request-body binding, codecs, and REST renderers now live in 
the optional
+`grails-xml` module. Newly generated applications and the web starter no 
longer include that module by
+default. MIME type negotiation still recognizes `application/xml`, `text/xml`, 
Atom, and other XML media
+types; recognizing a media type does not install an XML serializer.
+
+New REST controllers, RESTful controllers, resources, and scaffolded 
controllers generated by the
+`rest-api` profile advertise JSON only. After adding `grails-xml`, add `xml` 
to the artefact's

Review Comment:
   Done in b520a96840. `:grails-xml` is back in the starter-web `api` list, and 
`XML.render` logs one deprecation warning. Section 1 says the starter drops it 
and `@Resource` becomes JSON-only in Grails 10. `openapi-rest-api`, which only 
has the starter, gets XML through it again.



##########
grails-mimetypes/src/main/groovy/org/grails/web/mime/DefaultAcceptHeaderParser.groovy:
##########
@@ -123,8 +104,28 @@ class DefaultAcceptHeaderParser implements 
AcceptHeaderParser {
         mimes as MimeType[]
     }
 
-    protected void createMimeTypeAndAddToList(String name, MimeType[] 
mimeConfig, List<MimeType> mimes, Map<String,String> params = null) {
-        def mime = params ? new MimeType(name, params) : new MimeType(name)
+    protected List<MimeType> parseRequestedMimeTypes(String header) {
+        List<MimeType> mimeTypes = []
+
+        for (String token in header.split(',')) {
+            String candidate = token.trim()
+            try {
+                MediaType mediaType = MediaType.parseMediaType(candidate)
+                mimeTypes.add(SpringMediaTypeAdapter.toMimeType(mediaType))
+            }
+            catch (InvalidMediaTypeException ignored) {
+                // Preserve Grails' lenient handling of legacy headers such as 
a trailing semicolon,
+                // a valueless parameter, or an out-of-range/non-numeric 
quality value.
+                mimeTypes.add(new MimeType(candidate))

Review Comment:
   Fixed in fdd7fc102e. The `MimeType(String)` constructor splits with a limit 
of `-1` and skips parameters without a name, so `text/ html;` is ignored as 
before. `AcceptHeaderParserSpec` 'malformed tokens with empty parameters do not 
break negotiation' covers `text/ html;`, `text/ html;;`, `;` and `text/ html;=` 
ahead of a valid type.



##########
grails-mimetypes/src/main/groovy/org/grails/web/mime/GrailsMimeTypesWebMvcConfigurer.groovy:
##########
@@ -0,0 +1,74 @@
+/*
+ *  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.web.mime
+
+import groovy.transform.CompileStatic
+
+import grails.web.mime.MimeType
+
+import org.springframework.http.MediaType
+import 
org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
+
+/**
+ * Installs Grails format negotiation as the Spring MVC content negotiation 
strategy.
+ */
+@CompileStatic
+class GrailsMimeTypesWebMvcConfigurer implements WebMvcConfigurer {
+
+    private final GrailsContentNegotiationStrategy contentNegotiationStrategy
+
+    GrailsMimeTypesWebMvcConfigurer(GrailsContentNegotiationStrategy 
contentNegotiationStrategy) {
+        this.contentNegotiationStrategy = contentNegotiationStrategy
+    }
+
+    /**
+     * Exposes the configured strategy to Grails' own format resolution. The 
strategy is deliberately
+     * not a bean of its own: Spring Security adopts any {@link 
org.springframework.web.accept.ContentNegotiationStrategy}
+     * bean it finds, so it is reached through this configurer instead.
+     */
+    GrailsContentNegotiationStrategy getContentNegotiationStrategy() {
+        return contentNegotiationStrategy
+    }
+
+    @Override
+    void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
+        // Contribute the configured format aliases and nothing else. 
Replacing Spring's strategy
+        // list would hand Grails' parser authority over every Spring MVC 
endpoint: it drops media
+        // types absent from grails.mime.types and falls back to the defaults, 
which include */*, so
+        // a request for an unknown type would be answered instead of rejected 
with 406. It would
+        // also disable spring.mvc.contentnegotiation.* and apply the 'format' 
request parameter to
+        // endpoints that never asked for it. Grails' own format resolution 
does not go through the
+        // Spring manager; it uses this configurer's strategy directly.
+        Map<String, MediaType> aliases = [:]
+        for (MimeType mimeType in 
contentNegotiationStrategy.configuredMimeTypes) {
+            String extension = mimeType.extension
+            if (!extension || extension == MimeType.ALL.extension) {
+                continue
+            }
+            MediaType mediaType = SpringMediaTypeAdapter.toMediaType(mimeType)
+            if (mediaType != null && !mediaType.isWildcardType() && 
!mediaType.isWildcardSubtype()) {
+                aliases.putIfAbsent(extension, new MediaType(mediaType.type, 
mediaType.subtype))

Review Comment:
   Fixed in fdd7fc102e. The configurer is `@Order(HIGHEST_PRECEDENCE)`, so 
Boot's adapter and application configurers run after it and win. Where an 
extension maps to several types the `application/*` one is used, so `xml` is 
`application/xml`. `GrailsContentNegotiationStrategySpec` builds the manager 
from `MimeType.createDefaults()` (`?format=xml` resolves to `application/xml`). 
ab4b58eb68 starts an `@EnableWebMvc` context in which an `@Order(0)` configurer 
remaps `xml` and wins.



##########
grails-mimetypes/src/main/groovy/org/grails/web/mime/GrailsMimeTypesWebMvcConfigurer.groovy:
##########
@@ -0,0 +1,74 @@
+/*
+ *  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.web.mime
+
+import groovy.transform.CompileStatic
+
+import grails.web.mime.MimeType
+
+import org.springframework.http.MediaType
+import 
org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
+
+/**
+ * Installs Grails format negotiation as the Spring MVC content negotiation 
strategy.
+ */
+@CompileStatic
+class GrailsMimeTypesWebMvcConfigurer implements WebMvcConfigurer {
+
+    private final GrailsContentNegotiationStrategy contentNegotiationStrategy
+
+    GrailsMimeTypesWebMvcConfigurer(GrailsContentNegotiationStrategy 
contentNegotiationStrategy) {
+        this.contentNegotiationStrategy = contentNegotiationStrategy
+    }
+
+    /**
+     * Exposes the configured strategy to Grails' own format resolution. The 
strategy is deliberately
+     * not a bean of its own: Spring Security adopts any {@link 
org.springframework.web.accept.ContentNegotiationStrategy}
+     * bean it finds, so it is reached through this configurer instead.
+     */
+    GrailsContentNegotiationStrategy getContentNegotiationStrategy() {
+        return contentNegotiationStrategy
+    }
+
+    @Override
+    void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
+        // Contribute the configured format aliases and nothing else. 
Replacing Spring's strategy
+        // list would hand Grails' parser authority over every Spring MVC 
endpoint: it drops media
+        // types absent from grails.mime.types and falls back to the defaults, 
which include */*, so
+        // a request for an unknown type would be answered instead of rejected 
with 406. It would
+        // also disable spring.mvc.contentnegotiation.* and apply the 'format' 
request parameter to
+        // endpoints that never asked for it. Grails' own format resolution 
does not go through the
+        // Spring manager; it uses this configurer's strategy directly.
+        Map<String, MediaType> aliases = [:]
+        for (MimeType mimeType in 
contentNegotiationStrategy.configuredMimeTypes) {
+            String extension = mimeType.extension
+            if (!extension || extension == MimeType.ALL.extension) {
+                continue
+            }
+            MediaType mediaType = SpringMediaTypeAdapter.toMediaType(mimeType)
+            if (mediaType != null && !mediaType.isWildcardType() && 
!mediaType.isWildcardSubtype()) {
+                aliases.putIfAbsent(extension, new MediaType(mediaType.type, 
mediaType.subtype))
+            }
+        }
+        if (aliases) {
+            configurer.mediaTypes(aliases)

Review Comment:
   Done in fdd7fc102e: only `json`, `xml`, `hal`, `atom`, `rss`, `csv` and 
`text` are registered. The spec asserts that `html`, `js`, `css`, `pdf`, `form` 
and `multipartform` are not file extensions on the manager, and section 8 says 
so.



-- 
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