当前位置:首页 > 文章列表 > Golang > Go教程 > Go html/template 怎么安全把后端数据交给前端:别把 JSON 硬塞进 template.JS

Go html/template 怎么安全把后端数据交给前端:别把 JSON 硬塞进 template.JS

来源:17golang原创 2026-07-17 12:50:07 0浏览 收藏

做 Go 服务端渲染时,首屏经常要把用户信息、功能开关和分页条件交给一小段前端脚本。数据里一旦出现引号、换行或尖括号,很多人会先 json.Marshal,再把结果转成 template.JS 塞进页面。页面看起来立刻正常了,但这一步也绕开了 html/template 原本替你做的上下文转义。

要点速览

  • 把 Go 结构体直接放在普通 的 JavaScript 值位置,html/template 会按上下文生成可用的 JSON。
  • 不要把来自请求、数据库或第三方接口的 JSON 仅靠转为 template.JS 标记成安全内容。
  • 页面只需要一个短字段时,data-user-id 这类属性比塞一整段初始化对象更直观。
  • 回归时要带上引号、 片段和 Unicode 字符,确认页面没有长出额外标签或截断脚本。

问题不在 JSON,而在它被放进了哪个上下文

先看一个常见场景:服务端想让浏览器拿到当前用户和实验开关,于是在模板里写 const pageData = {{ .Page }};。这不是简单的字符串替换。html/template 会识别当前动作位于 JavaScript 上下文;传入的是结构体或 map 时,它会生成适合该位置的 JSON。这个能力的前提是模板由可信开发者编写,而渲染数据可以是不可信的。

真正危险的捷径是把一段外部 JSON 直接包装成 template.JS。这个类型表示“已知安全的 JavaScript 表达式”,责任随之落到调用方。官方文档明确提醒:即便 JSON 在语法上有效,只要数据不可信,就不该因为它像 JSON 而跳过模板的处理。

最小配方:让结构体进入 script 的值位置

先不要手工拼引号,也不用预先把对象编码成字符串。给视图模型一个清晰的边界,再把它直接交给模板。下面是模板片段与数据类型;项目里的渲染入口照常把 Page 作为数据的一部分传入即可。

package view

import "html/template"

type User struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

type PageData struct {
	User  User     `json:"user"`
	Flags []string `json:"flags"`
}

type PageModel struct {
	Page PageData
}

const pageHTML = `
` func parsePage() (*template.Template, error) { return template.New("profile").Parse(pageHTML) }

关键是 {{ .Page }} 前后没有再包一层手写引号。PageData 不是普通文本,它在脚本值位置被模板按 JavaScript 规则处理。先在本地用用户名 Li "A" 和包含换行的备注走一遍页面:浏览器应该拿到同一个数据值,但 DOM 中不能多出一段意外的标签或脚本。

Go 结构体经过 html/template 进入 pageData 并被浏览器安全读取的低饱和时间线图
结构体不是先变成一段可拼接文本,而是让模板根据 script 的上下文把数据交给 pageData。

前端读取时,把初始化对象当数据而不是模板碎片

有了 pageData,前端代码就按普通对象读取。这个阶段最容易被忽略的细节是:把数据用于 textContent、属性赋值或受控的组件状态,而不是拿字符串再拼一段 HTML。这样前后端的责任也更清楚——Go 负责输出结构化初始数据,浏览器代码负责按 DOM API 渲染。

const badge = document.querySelector("#profile");

if (pageData.flags.includes("beta")) {
  badge.dataset.mode = "beta";
}

badge.textContent = `${pageData.user.name} #${pageData.user.id}`;

这一步可以顺手做一个核对:打开浏览器开发者工具,确认 pageData 是对象而不是一串双重编码的 JSON 字符串;再确认页面显示的是原始文字,不是 " 一类转义残留。若拿到的是字符串,通常是某层又做了一次 json.Marshal,而不是模板失效。

只传一个短字段时,data-* 往往更省心

并非每个页面都需要一个初始化对象。比如一个“编辑资料”按钮只要当前用户 ID,直接写到 data-user-id 更容易审查,也不必让页面多一段脚本数据。属性值仍由 html/template 处理,前端用 dataset 读取即可。



这里的边界很实际:少量标量放属性没问题;筛选器、权限集合、嵌套对象或上百条列表,不适合拆成一堆 data-*。数据开始变复杂时,回到上面的结构体配方,或者通过一个受认证的 JSON 接口按需加载。

Go 模板将 userID 写入 data-user-id 并由浏览器 dataset 读取的低饱和时间线图
一个字段走 data-*,复杂对象走 script 值位置;两条路径都不需要把不可信字符串标成 template.JS。

template.JS 不是给 JSON 开的绿色通道

template.JS 仍有合理用途,例如模板作者自己写死的一小段 JavaScript 表达式,并且能证明内容不含外部输入。它不是“JSON 字符串专用类型”,更不是给数据库字段、查询参数、CMS 内容或上游接口返回值消除转义的工具。

如果上游给的是 JSON 文本,先用 json.Unmarshal 解析成 Go 结构体或 map,再把该对象传给模板。解析失败就把它当作数据错误处理,而不是为了让页面先出来而原样塞进脚本。这个取舍看似多一步,却让类型、错误和安全边界都留在服务端。

上线前用四个输入检查渲染结果

模板安全问题往往不会在普通中文用户名上暴露。给页面准备四组固定样本,检查响应源码和浏览器显示,比只看“页面能打开”可靠得多:

  • 普通值:Lin,确认对象字段和页面文字都正常。
  • 引号值:Lin "A",确认脚本没有提前结束,JavaScript 仍能读取对象。
  • 标签样本:包含 的文本,确认浏览器没有新增可运行标签。
  • Unicode 与换行:确认中文、emoji 和多行备注不会变成双重编码字符串。

同时别忘了确认服务使用的是 html/template 而不是 text/template。两者 API 很像,但只有前者会针对 HTML、JavaScript、CSS 和 URL 上下文处理输出。升级 Go 版本时,也应把这组样本放进回归测试,尤其是项目有大量 script 模板片段时。

相关问题

能把 JSON 放进 type="application/json" 的 script 标签吗?

可以作为另一种页面数据载体,但要先验证所用 Go 版本与模板写法的上下文处理结果。若只是把 Go 对象交给页面的 JavaScript,普通 script 的值位置配合 {{ .Page }} 更直接,也更容易按官方文档理解。

data-* 能放一个完整 JSON 对象吗?

不建议作为默认方案。属性适合短字段;对象越大,转义、长度和调试成本越高。复杂数据用结构体进入 script 值位置,或通过 API 按需请求更清晰。

template.HTML 和 template.JS 的风险一样吗?

两者都是“已知安全内容”的显式标记,都会把审查责任交给调用方。只有内容确实由可信模板作者固定写死,且能证明不会混入外部数据时,才考虑使用。

为什么页面里的对象字段名不是我想要的小写?

为 Go 的结构体字段补上 json 标签,例如 json:"user"。这样模板在 JavaScript 上下文序列化时能得到稳定、便于前端读取的字段名。

把安全边界留在模板上下文里

服务端渲染页面并不排斥前端交互,关键是别把后端数据降成一段手工拼接的脚本文本。对象就按对象传给 html/template,短字段就用 data-*;只有真正可信的表达式才考虑 typed string。这样处理后,用户输入再复杂,模板上下文仍是那条最可靠的边界。

关于 JavaScript 上下文、typed string 和安全模型,可继续阅读 Go 官方的 html/template 文档包说明源码

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 服务怎么用 Sec-Fetch-Site 拦住跨站请求:Fetch Metadata 的最小中间件Go 服务怎么用 Sec-Fetch-Site 拦住跨站请求:Fetch Metadata 的最小中间件
上一篇
Go 服务怎么用 Sec-Fetch-Site 拦住跨站请求:Fetch Metadata 的最小中间件
Java JTable 双击怎么拿到正确行:MouseAdapter、排序转换和空白行判断
下一篇
Java JTable 双击怎么拿到正确行:MouseAdapter、排序转换和空白行判断
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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推荐
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    120次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    40次使用
  • Google AI提示词库:免费官方Prompt模板与使用指南
    Google AI提示词库
    探索Google Cloud官方生成式AI提示词库,提供免费、无需登录的中英双语Prompt模板。涵盖内容创作、代码优化、数据分析等场景,助您快速提升AI交互效率与质量。
    16次使用
  • Gradio是什么?Python开源库快速构建机器学习Web演示界面
    Gradio
    Gradio是一个用于构建机器学习和数据科学Web应用的开源Python库。支持快速创建交互界面,获Google、Meta等大厂青睐,适合模型演示、部署反馈及调试。
    116次使用
  • AgentGPT是什么?开源自主AI代理工具详解与本地部署指南
    AgentGPT
    深入了解AgentGPT:一款基于浏览器的自主人工智能代理工具。本文解析其核心功能、技术栈、应用场景,并提供详细的在线使用及本地部署教程,助您高效利用AI自动化完成任务。
    15次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码