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]