当前位置:首页 > 文章列表 > Golang > Go教程 > Go text/template与html/template选择输出边界的对比

Go text/template与html/template选择输出边界的对比

来源:17golang原创 2026-09-23 13:13:29 0浏览 收藏

我第一次把 Go 模板接进一个小型管理页时,真正容易混淆的不是 {{.Title}} 怎么写,而是“这段输出最终给谁消费”。日志、邮件正文和配置文件需要保留文本;浏览器 HTML 则必须让数据留在正确的 HTML、URI 或 JavaScript 上下文里。两套包的解析和执行 API 很像,选择却不能只看调用习惯。

结论很明确:输出是纯文本时使用 text/template;输出会进入 HTML 页面时使用 html/template。后者会根据上下文自动转义普通数据,不能用前者渲染网页,也不要把手动转义当成替代方案。

这篇用同一份数据做一个最小对比项目,重点看选择依据、代码差异、转义位置和可信 HTML 的边界。

项目目标:先按输出边界做选择

先不要急着把两个包抽象成同一个渲染器。我会先写一张输出清单:命令行日志、邮件纯文本、Markdown 或配置片段属于文本输出;服务端生成的网页属于 HTML 输出。前一组可以使用 text/template,后一组直接使用 html/template

最终消费者选择关键理由
日志、邮件、文本文件text/template模板包把动作结果按文本写出,不主动做 HTML 上下文转义
浏览器 HTMLhtml/template按 HTML、CSS、JavaScript、URI 上下文处理普通数据
受信任的 HTML 片段谨慎使用 template.HTML只在片段来源和拼接过程都由受信任代码控制时保留标记
text/template与html/template输出边界结构说明图
图1:输出边界结构说明图,静态展示数据、模板包与纯文本/HTML消费者的关系,不是运行截图。

核心代码:分别解析并执行两种模板

下面的示例让两种模板共享数据结构和模板内容,只替换 import 路径。代码中的错误不能省略:Parse 负责模板语法,Execute 负责把数据写入目标;两处失败都应该交给调用方处理。

package main

import (
    "bytes"
    "fmt"
    htmltmpl "html/template"
    texttmpl "text/template"
)

type PageData struct {
    Title string
    Link  string
}

const pageTemplate = `

{{.Title}}

查看详情` func renderText(data PageData) (string, error) { // 纯文本输出不承担 HTML 上下文安全职责,适合日志或邮件正文。 tmpl, err := texttmpl.New("text").Parse(pageTemplate) if err != nil { return "", fmt.Errorf("解析文本模板: %w", err) } var buf bytes.Buffer // Execute 失败时返回部分结果也不能被当成完整页面继续使用。 if err := tmpl.Execute(&buf, data); err != nil { return "", fmt.Errorf("执行文本模板: %w", err) } return buf.String(), nil } func renderHTML(data PageData) (string, error) { // HTML 输出交给 html/template,根据值所在的上下文做转义。 tmpl, err := htmltmpl.New("html").Parse(pageTemplate) if err != nil { return "", fmt.Errorf("解析 HTML 模板: %w", err) } var buf bytes.Buffer // 保留错误返回,让上层决定记录、重试还是返回 HTTP 错误。 if err := tmpl.Execute(&buf, data); err != nil { return "", fmt.Errorf("执行 HTML 模板: %w", err) } return buf.String(), nil }

实际 Go 源文件中的模板字符串应使用原始反引号,并把 HTML 标签直接写入字符串;上面为了让文章页面保留标签,展示时对尖括号做了 HTML 实体编码。两种函数的关键差异只有包路径,但这正是安全语义的分界。

本地运行:用同一份数据观察转义结果

测试数据要刻意包含标签字符和一个带查询参数的链接。不要把“页面看起来能打开”当作结论,应分别观察文本节点和属性值。

func main() {
    data := PageData{
        // 用特殊字符验证普通文本与 HTML 上下文的差异。
        Title: `内部公告`,
        Link:  `/search?q=go&mode=full`,
    }

    plain, err := renderText(data)
    if err != nil {
        panic(err)
    }
    page, err := renderHTML(data)
    if err != nil {
        panic(err)
    }
    fmt.Println("text/template:", plain)
    fmt.Println("html/template:", page)
}

text/template 中,数据更接近原样写入;如果这段结果直接作为 HTML 响应,数据中的标记就可能改变页面结构。html/template 会把标题中的尖括号编码成文本,并在 href 这样的 URI/属性上下文中应用对应处理。它不是“把所有字符无脑替换”,而是根据模板位置计算上下文。

HTML文本节点与href属性上下文转义关系说明图
图2:上下文转义关系说明图,静态展示普通数据进入 HTML 文本节点与 href 属性后的边界,不是运行结果截图。

验收结论:把 HTML、纯文本和可信片段分开

最后我会把选择规则写进代码评审清单。第一,模板作者可以是可信的,但 Execute 接收的数据默认不可信;第二,网页输出统一从 html/template 开始,不用 text/template 生成 HTML;第三,template.HTML 是信任承诺,不是“关闭转义”的快捷按钮。

只有在 HTML 片段由受信任代码构造、结构经过明确控制,而且调用方确实需要保留标签时,才考虑类型化的安全内容。用户输入、数据库字段或外部接口返回值不要直接转换成 template.HTML。同样,html/template 中也不必手动套 htmlurlquery 来“加固”,让包根据上下文处理更容易维护。

速记:文本消费者选 text/template,HTML 消费者选 html/template;先判断输出边界,再判断数据是否可信,最后才考虑模板函数或安全类型。

相关问题

两个包的模板语法能共用吗

大多数基础动作和执行接口相同,因此可以共用不少模板文本,但最终输出格式和安全职责不同,不能因为语法相同就互换包。

为什么 html/template 输出里出现了转义实体

普通字符串被当作数据而不是模板作者写入的标记。转成实体后,浏览器会把它显示为文本,从而保持 HTML 结构不被数据改变。

什么时候可以使用 template.HTML

仅限于来源、拼接逻辑和允许的标签都由受信任代码控制的片段;不应把外部输入通过类型转换直接标记为安全 HTML。

参考资料:https://pkg.go.dev/text/templatehttps://pkg.go.dev/html/templatehttps://go.dev/src/html/template/doc.go

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go copy返回长度小于预期时的切片容量分析Go copy返回长度小于预期时的切片容量分析
上一篇
Go copy返回长度小于预期时的切片容量分析
Go time.Timer复用时停止旧计时并排空通道的写法
下一篇
Go time.Timer复用时停止旧计时并排空通道的写法
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    187次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    243次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    201次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    183次使用
  • CMMLU中文大模型评估基准:功能、使用教程与应用场景解析
    CMMLU
    深入了解CMMLU中文评估基准,涵盖67个学科主题,提供数据集下载、Zero-shot/Five-shot评估方法及排行榜,助力优化中文语言模型性能。
    172次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码