在 Freemarker 模板中调用 Java 方法返回 HTML 片段时,需避免自动转义导致标签被显示为纯文本;正确做法是使用 HTMLOutputFormat.INSTANCE.fromMarkup() 包装返回值,使 Freemarker 识别其为可信 HTML 并原样渲染。
在 freemarker 模板中调用 java 方法返回 html 片段时,需避免自动转义导致标签被显示为纯文本;正确做法是使用 `htmloutputformat.instance.frommarkup()` 包装返回值,使 freemarker 识别其为可信 html 并原样渲染。
在 Freemarker 中,出于安全默认启用 HTML 自动转义(auto-escaping),这意味着所有普通 String 类型变量在通过 ${...} 插入模板时,尖括号、引号等会被转义为 <、" 等实体——这能有效防止 XSS,但也会导致你精心拼接的
因此,不能直接返回 StringBuilder.toString() 的原始字符串,即使当前看似“能工作”,也极可能是因为模板未启用 auto-escaping(如配置了 template.setEncoding("UTF-8") 但未设 template.setOutputFormat(HTMLOutputFormat.INSTANCE)),这种行为不可靠且存在严重安全风险。
✅ 正确实践:在 Java 方法中显式标记 HTML 内容为“已信任的 HTML”:
import freemarker.template.TemplateHTMLModel;
import freemarker.template.utility.HTMLOutputFormat;
public class TemplateHelper {
private final Map<string list>> elements; // 假设你的数据结构
public TemplateHelper(Map<string list>> elements) {
this.elements = elements;
}
public TemplateHTMLModel renderRow(String key) {
StringBuilder sb = new StringBuilder();
List<element> entries = elements.get(key);
if (entries != null && !entries.isEmpty()) {
for (Element e : entries) {
sb.append("<tr>")
.append("<td>").append(e.getNumber()).append("</td>")
.append("<td>").append(e.getName()).append("</td>")
.append("<td>").append(e.getDescription()).append("</td>")
.append("</tr>");
}
} else {
// 渲染空行占位
sb.append("<tr><td colspan='\"3\"'>—</td></tr>");
}
// 关键:用 HTMLOutputFormat 包装,告知 FreeMarker 此字符串应作为 HTML 渲染
return HTMLOutputFormat.INSTANCE.fromMarkup(sb.toString());
}
}</element></string></string>
然后在 .ftl 模板中直接调用(注意使用 ${},不是 #{}):
| 编号 | 名称 | 描述 |
|---|
⚠️ 重要注意事项:
- 绝不手动拼接未转义的用户输入:若 e.getName() 等字段来自外部(如数据库、表单),必须先对原始数据做 HTML 转义(如用 StringEscapeUtils.escapeHtml4()),再拼入 HTML 模板字符串;fromMarkup() 仅表示“此字符串整体可安全渲染”,不负责子内容净化。
-
模板需启用 HTML 输出格式:确保 FreeMarker 配置中为 HTML 模板设置了正确的输出格式,例如:
cfg.setTemplateLoader(...); cfg.setDefaultEncoding("UTF-8"); cfg.setOutputFormat(HTMLOutputFormat.INSTANCE); // 必须设置! cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER); - 避免滥用:仅对完全由服务端可控、无用户直写内容的 HTML 片段使用 fromMarkup();复杂 UI 建议仍优先使用 FTL 原生指令( + )配合数据预处理,保持逻辑与视图分离。
总结:Java 层返回 HTML 字符串本身不是问题,关键在于语义标注——用 HTMLOutputFormat.INSTANCE.fromMarkup() 显式声明其 HTML 含义,并配合全局 HTML 输出格式配置,才能既保证安全性,又实现灵活的模板逻辑下放。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











