From b64edb2d2abf5068a0d5fa0289a809aa53803f0f Mon Sep 17 00:00:00 2001
From: Sam Brannen <104798+sbrannen@users.noreply.github.com>
Date: Sat, 29 Jun 2024 16:20:34 +0200
Subject: [PATCH] Update Content-Type based on encoding in MVC FreeMarkerView
Closes gh-33119
---
.../pages/web/webmvc-view/mvc-freemarker.adoc | 3 +
.../FreeMarkerConfigurationFactory.java | 17 ++--
.../FreeMarkerConfigurationFactoryBean.java | 2 +-
.../view/freemarker/FreeMarkerConfig.java | 2 +
.../view/freemarker/FreeMarkerConfigurer.java | 2 +-
.../view/freemarker/FreeMarkerView.java | 4 +-
.../freemarker/FreeMarkerViewResolver.java | 4 +-
.../view/freemarker/FreeMarkerConfig.java | 2 +
.../view/freemarker/FreeMarkerConfigurer.java | 2 +-
.../view/freemarker/FreeMarkerView.java | 20 ++---
.../freemarker/FreeMarkerViewResolver.java | 77 ++++++++++++++++++-
.../ViewResolutionIntegrationTests.java | 53 ++++++-------
12 files changed, 134 insertions(+), 54 deletions(-)
diff --git a/framework-docs/modules/ROOT/pages/web/webmvc-view/mvc-freemarker.adoc b/framework-docs/modules/ROOT/pages/web/webmvc-view/mvc-freemarker.adoc
index e0e95083b98..7944f7c42bb 100644
--- a/framework-docs/modules/ROOT/pages/web/webmvc-view/mvc-freemarker.adoc
+++ b/framework-docs/modules/ROOT/pages/web/webmvc-view/mvc-freemarker.adoc
@@ -36,6 +36,7 @@ Java::
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
+ configurer.setDefaultCharset(StandardCharsets.UTF_8);
return configurer;
}
}
@@ -58,6 +59,7 @@ Kotlin::
@Bean
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
setTemplateLoaderPath("/WEB-INF/freemarker")
+ setDefaultCharset(StandardCharsets.UTF_8)
}
}
----
@@ -86,6 +88,7 @@ properties, as the following example shows:
----
The simplest way to use this class is to specify a "templateLoaderPath"; * FreeMarker does not need any further configuration then. * - *
Note: Spring's FreeMarker support requires FreeMarker 2.3.21 or higher. + *
Note: Spring's FreeMarker support requires FreeMarker 2.3.26 or higher. * * @author Darren Davison * @author Juergen Hoeller @@ -143,15 +143,18 @@ public class FreeMarkerConfigurationFactory { * files. *
If not specified, FreeMarker will read template files using the platform * file encoding (defined by the JVM system property {@code file.encoding}) - * or {@code "utf-8"} if the platform file encoding is undefined. - *
Note that the encoding is not used for template rendering. Instead, an - * explicit encoding must be specified for the rendering process — for - * example, via Spring's {@code FreeMarkerView} or {@code FreeMarkerViewResolver}. + * or UTF-8 if the platform file encoding is undefined. + *
Note that the supplied encoding may or may not be used for template + * rendering. See the documentation for Spring's {@code FreeMarkerView} and + * {@code FreeMarkerViewResolver} implementations for further details. * @see #setDefaultEncoding(Charset) * @see freemarker.template.Configuration#setDefaultEncoding * @see org.springframework.web.servlet.view.freemarker.FreeMarkerView#setEncoding * @see org.springframework.web.servlet.view.freemarker.FreeMarkerView#setContentType * @see org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver#setContentType + * @see org.springframework.web.reactive.result.view.freemarker.FreeMarkerView#setEncoding + * @see org.springframework.web.reactive.result.view.freemarker.FreeMarkerView#setSupportedMediaTypes + * @see org.springframework.web.reactive.result.view.freemarker.FreeMarkerViewResolver#setSupportedMediaTypes */ public void setDefaultEncoding(String defaultEncoding) { this.defaultEncoding = defaultEncoding; @@ -170,7 +173,7 @@ public class FreeMarkerConfigurationFactory { } /** - * Set a List of {@link TemplateLoader TemplateLoaders} that will be used to + * Set a list of {@link TemplateLoader TemplateLoaders} that will be used to * search for templates. *
For example, one or more custom loaders such as database loaders could * be configured and injected here. @@ -186,7 +189,7 @@ public class FreeMarkerConfigurationFactory { } /** - * Set a List of {@link TemplateLoader TemplateLoaders} that will be used to + * Set a list of {@link TemplateLoader TemplateLoaders} that will be used to * search for templates. *
For example, one or more custom loaders such as database loaders could * be configured and injected here. diff --git a/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerConfigurationFactoryBean.java b/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerConfigurationFactoryBean.java index 3aac7d4df21..a98666a2a57 100644 --- a/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerConfigurationFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerConfigurationFactoryBean.java @@ -45,7 +45,7 @@ import org.springframework.lang.Nullable; *
See the {@link FreeMarkerConfigurationFactory} base class for configuration * details. * - *
Note: Spring's FreeMarker support requires FreeMarker 2.3.21 or higher. + *
Note: Spring's FreeMarker support requires FreeMarker 2.3.26 or higher. * * @author Darren Davison * @since 03.03.2004 diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerConfig.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerConfig.java index e30c3d9b601..7a4ede11193 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerConfig.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerConfig.java @@ -24,6 +24,8 @@ import freemarker.template.Configuration; * *
Detected and used by {@link FreeMarkerView}. * + *
Note: Spring's FreeMarker support requires FreeMarker 2.3.26 or higher. + * * @author Rossen Stoyanchev * @since 5.0 */ diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerConfigurer.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerConfigurer.java index 76b54358ac3..b1a4318837e 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerConfigurer.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerConfigurer.java @@ -56,7 +56,7 @@ import org.springframework.util.Assert; * <@spring.bind "person.age"/> * age is ${spring.status.value} * - *
Note: Spring's FreeMarker support requires FreeMarker 2.3.21 or higher. + *
Note: Spring's FreeMarker support requires FreeMarker 2.3.26 or higher. * * @author Rossen Stoyanchev * @since 5.0 diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerView.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerView.java index 89386db8958..955381dc164 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerView.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerView.java @@ -90,7 +90,7 @@ import org.springframework.web.server.ServerWebExchange; * sets the supported media type to {@code "text/html;charset=UTF-8"} by default. * Thus, those default values are likely suitable for most applications. * - *
Note: Spring's FreeMarker support requires FreeMarker 2.3.21 or higher. + *
Note: Spring's FreeMarker support requires FreeMarker 2.3.26 or higher. * * @author Rossen Stoyanchev * @author Sam Brannen @@ -158,7 +158,7 @@ public class FreeMarkerView extends AbstractUrlBasedView { *
If the encoding is not explicitly set here or in the FreeMarker * {@code Configuration}, FreeMarker will read template files using the platform * file encoding (defined by the JVM system property {@code file.encoding}) - * or {@code "utf-8"} if the platform file encoding is undefined. Note, + * or UTF-8 if the platform file encoding is undefined. Note, * however, that {@link FreeMarkerConfigurer} sets the default encoding in the * FreeMarker {@code Configuration} to "UTF-8". *
It's recommended to specify the encoding in the FreeMarker {@code Configuration} diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerViewResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerViewResolver.java index 89e021229e7..eb5663a4404 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerViewResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/view/freemarker/FreeMarkerViewResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,8 @@ import org.springframework.web.reactive.result.view.UrlBasedViewResolver; *
The view class for all views generated by this resolver can be specified * via the "viewClass" property. See {@link UrlBasedViewResolver} for details. * + *
Note: Spring's FreeMarker support requires FreeMarker 2.3.26 or higher. + * * @author Rossen Stoyanchev * @since 5.0 */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerConfig.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerConfig.java index abf21250472..e6b69901343 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerConfig.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerConfig.java @@ -24,6 +24,8 @@ import freemarker.template.Configuration; * *
Detected and used by {@link FreeMarkerView}. * + *
Note: Spring's FreeMarker support requires FreeMarker 2.3.26 or higher. + * * @author Darren Davison * @author Rob Harrop * @since 03.03.2004 diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerConfigurer.java index 3943bd1898b..24c1aa9445f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerConfigurer.java @@ -62,7 +62,7 @@ import org.springframework.util.Assert; * <@spring.bind "person.age"/> * age is ${spring.status.value} * - *
Note: Spring's FreeMarker support requires FreeMarker 2.3.21 or higher. + *
Note: Spring's FreeMarker support requires FreeMarker 2.3.26 or higher. * * @author Darren Davison * @author Rob Harrop diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerView.java index 533a398be21..0a1c2bee026 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerView.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerView.java @@ -56,8 +56,8 @@ import org.springframework.web.servlet.view.AbstractTemplateView; * byte sequences to character sequences when reading the FreeMarker template file. * Default is determined by the FreeMarker {@link Configuration}. *
Note: Spring's FreeMarker support requires FreeMarker 2.3.21 or higher. + *
Note: Spring's FreeMarker support requires FreeMarker 2.3.26 or higher. * As of Spring Framework 6.0, FreeMarker templates are rendered in a minimal * fashion without JSP support, just exposing request attributes in addition * to the MVC-provided model map for alignment with common Servlet resources. @@ -109,13 +113,11 @@ public class FreeMarkerView extends AbstractTemplateView { *
If the encoding is not explicitly set here or in the FreeMarker * {@code Configuration}, FreeMarker will read template files using the platform * file encoding (defined by the JVM system property {@code file.encoding}) - * or {@code "utf-8"} if the platform file encoding is undefined. + * or UTF-8 if the platform file encoding is undefined. *
It's recommended to specify the encoding in the FreeMarker {@code Configuration} * rather than per template if all your templates share a common encoding. - *
Note that the specified or default encoding is not used for template - * rendering. Instead, an explicit encoding must be specified for the rendering - * process. See the note in the {@linkplain FreeMarkerView class-level - * documentation} for details. + *
See the note in the {@linkplain FreeMarkerView class-level documentation} + * for details regarding the encoding used to render the response. * @see freemarker.template.Configuration#setDefaultEncoding * @see #setCharset(Charset) * @see #getEncoding() diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerViewResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerViewResolver.java index 17c728dfe6a..9fed609f101 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerViewResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/freemarker/FreeMarkerViewResolver.java @@ -16,6 +16,13 @@ package org.springframework.web.servlet.view.freemarker; +import java.util.Locale; + +import freemarker.template.Configuration; + +import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.View; import org.springframework.web.servlet.view.AbstractTemplateViewResolver; import org.springframework.web.servlet.view.AbstractUrlBasedView; @@ -29,12 +36,19 @@ import org.springframework.web.servlet.view.AbstractUrlBasedView; *
Note: To ensure that the correct encoding is used when the rendering * the response, set the {@linkplain #setContentType(String) content type} with an * appropriate {@code charset} attribute — for example, - * {@code "text/html;charset=UTF-8"}. + * {@code "text/html;charset=UTF-8"}; however, as of Spring Framework 6.2, it is + * no longer strictly necessary to explicitly set the content type in the + * {@code FreeMarkerViewResolver} if you have set an explicit encoding via either + * {@link FreeMarkerView#setEncoding(String)}, + * {@link FreeMarkerConfigurer#setDefaultEncoding(String)}, or + * {@link Configuration#setDefaultEncoding(String)}. * *
Note: When chaining ViewResolvers, a {@code FreeMarkerViewResolver} will * check for the existence of the specified template resources and only return * a non-null {@code View} object if the template was actually found. * + *
Note: Spring's FreeMarker support requires FreeMarker 2.3.26 or higher. + * * @author Juergen Hoeller * @author Sam Brannen * @since 1.1 @@ -83,4 +97,65 @@ public class FreeMarkerViewResolver extends AbstractTemplateViewResolver { return (getViewClass() == FreeMarkerView.class ? new FreeMarkerView() : super.instantiateView()); } + /** + * Delegates to {@code super.loadView(viewName, locale)} for standard behavior + * and then to {@link #postProcessView(FreeMarkerView)} for customization. + * @since 6.2 + * @see org.springframework.web.servlet.view.UrlBasedViewResolver#loadView(String, Locale) + * @see #postProcessView(FreeMarkerView) + */ + @Override + @Nullable + protected View loadView(String viewName, Locale locale) throws Exception { + View view = super.loadView(viewName, locale); + if (view instanceof FreeMarkerView freeMarkerView) { + postProcessView(freeMarkerView); + } + return view; + } + + /** + * Post process the supplied {@link FreeMarkerView} after it has been {@linkplain + * org.springframework.web.servlet.view.UrlBasedViewResolver#loadView(String, Locale) + * loaded}. + *
The default implementation attempts to override the
+ * {@linkplain org.springframework.web.servlet.view.AbstractView#setContentType(String)
+ * content type} of the view with {@code "text/html;charset=