You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring MVC中Freemarker调用spring.ftl时springMacroRequestContext为空报错

Spring MVC中Freemarker使用spring.ftl宏时springMacroRequestContext为空问题

在Spring MVC环境下,通过Freemarker HTML模板使用spring.ftl提供的通用工具宏读取资源文件标签时,出现springMacroRequestContext为空的错误。已在FreeMarkerViewResolver中设置setExposeSpringMacroHelpers(true),但问题仍未解决。

配置代码

package com.omb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;

import java.util.Properties;

@Configuration
@EnableWebMvc
public class FreeMarkerConfiguration implements WebMvcConfigurer {

    @Bean
    public FreeMarkerViewResolver viewResolver() {
        FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver();
        viewResolver.setExposeSpringMacroHelpers(true);
        viewResolver.setExposeRequestAttributes(true);
        viewResolver.setPrefix("");
        viewResolver.setSuffix(".ftl");
        viewResolver.setContentType("text/html;charset=UTF-8");
        return viewResolver;
    }

    @Bean
    public FreeMarkerConfigurer freemarkerConfig() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setDefaultEncoding("UTF-8");
        configurer.setTemplateLoaderPath("/WEB-INF/views/ftl/");

        Properties settings = new Properties();
        settings.setProperty("auto_import", "/spring.ftl as spring");
        settings.setProperty("incompatible_improvements", "2.3.32");
        settings.setProperty("template_exception_handler", "rethrow");
        configurer.setFreemarkerSettings(settings);
        return configurer;
    }
}

服务注入配置

@Autowired
private FreeMarkerConfiguration freeMarkerConfiguration;

模板调用方法

public static byte[] generateDoc(String templateName, String templateDocument, Map<String, Object> objects, Map<String, Object> params, FreeMarkerConfiguration configuration) throws IOException, TemplateException {
        ConverterProperties converterProperties = getConverterProperties();

        Template template = new Template(templateName, new StringReader(templateDocument),  configuration.freemarkerConfig().createConfiguration());

        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            Writer writer = new OutputStreamWriter(out);

            Environment env = template.createProcessingEnvironment(objects, writer);
            populateEnvironnment(env);
            env.process();

            ByteArrayOutputStream fos = new ByteArrayOutputStream();
            PdfDocument output = new PdfDocument(new PdfWriter(fos));
            output.setDefaultPageSize((PageSize) ownParams.get(PAGE_SIZE));

            try (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray())) {
                HtmlConverter.convertToPdf(in, output, converterProperties);
            }
            return fos.toByteArray();
        }
    }

HTML模板代码

<#import "/spring.ftl" as spring/>

<!DOCTYPE html>
<html>

<body>
<@spring.messageText "resource.bundle.key"/>
</body>
</html>

错误信息

The following has evaluated to null or missing:
==> springMacroRequestContext  [in template "spring.ftl" at line 28, column 24]

----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
    - Failed at: ${springMacroRequestContext.getMessag...  [in template "spring.ftl" in macro "message" at line 28, column 22]
    - Reached through: @spring.message "sale.enum.TravelPass...  [in template "MY_TEMPLATE" at line 7, column 9]
----
FreeMarker template error:
The following has evaluated to null or missing:
==> springMacroRequestContext  [in template "spring.ftl" at line 28, column 24]

----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
    - Failed at: ${springMacroRequestContext.getMessag...  [in template "spring.ftl" in macro "message" at line 28, column 22]
    - Reached through: @spring.message "resource.bundle.key...  [in template "MY_TEMPLATE" at line 53, column 9]
----

Java stack trace (for programmers):
----
freemarker.core.InvalidReferenceException: [... Exception message was already printed; see it above ...]
    at freemarker.core.InvalidReferenceException.getInstance(InvalidReferenceException.java:134)
    at freemarker.core.UnexpectedTypeException.newDescriptionBuilder(UnexpectedTypeException.java:85)
    at freemarker.core.UnexpectedTypeException.<init>(UnexpectedTypeException.java:48)
    at freemarker.core.NonHashException.<init>(NonHashException.java:49)
    at freemarker.core.Dot._eval(Dot.java:48)

临时解决方案

目前找到一个临时解决办法,但不够优雅:手动向数据模型Map中添加RequestContext。

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
map.put("springMacroRequestContext", new RequestContext(request, null, null, null));

内容的提问来源于stack exchange,提问作者Ousmane MBINTE

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 15:27:16