
在 Go 模板中直接渲染含 标签的富文本(如 @用户名链接)时,默认会转义 HTML 字符导致链接失效;需用 template.HTML 显式标记可信 HTML,但必须严格防范 XSS。
在 go 模板中直接渲染含 `` 标签的富文本(如 @用户名链接)时,默认会转义 html 字符导致链接失效;需用 `template.html` 显式标记可信 html,但必须严格防范 xss。
Go 的 html/template 包默认对所有变量插值执行 HTML 转义(如将 ds" 构造完成并赋值给 .Msg,模板仍会将其作为纯文本输出,而非可点击的链接。
正确做法:在服务端将处理后的字符串显式转换为 template.HTML 类型:
// 在 handler 中(非模板内!)
import "html/template"
// 假设 msgStr 是已处理好的带 HTML 的字符串
msgStr := `@<a class="tweet-url username" href="https://www.php.cn/link/bf87e03ccaa859089a302a8bb61a9f00" data-screen-name="ds" rel="nofollow">ds</a>`
// 关键:用 template.HTML 包装,告知模板“此内容已安全,无需转义”
data := struct {
Msg template.HTML
}{
Msg: template.HTML(msgStr),
}
tmpl.Execute(w, data)
随后模板即可正常渲染:
<div class="panel-body">
<p>{{.Msg}}</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill5806" title="html-deploy"><img
src="https://img.php.cn/upload/skill/000/000/081/179066538882434.jpg" alt="html-deploy" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill5806" title="html-deploy" class="overflowclass">html-deploy</a>
<p class="overflowclass">使用 htmlcode.fun 将 HTML 内容或文件部署到网页,适用于用户要求“部署到网页”“托管此 HTML”“生成此前端...的实时链接”等场景。</p>
</div>
<a rel="nofollow" href="/xiazai/skill5806" title="html-deploy" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
</div>
✅ 输出效果:@ds(其中 ds 为可点击链接)
❌ 错误写法(不包装):@<a class="...">ds</a>(纯文本)
⚠️ 重要安全提醒:
- template.HTML 绕过了自动转义,仅适用于完全可控、已净化的 HTML 字符串;
- 若原始输入含用户提交内容(如 @<script>alert(1)</script>),直接包装将导致严重 XSS;
- 推荐实践:
- 输入时净化:使用 bluemonday 或 gorilla/securecookie 等库对用户输入做白名单过滤(仅允许 及指定属性);
- 存储前转义:如答案中提到的“先 html.EscapeString 再存 DB”,避免脏数据污染持久层;
- 渲染前构造:从干净文本(如 @ds)出发,在服务端解析并生成受限 HTML(如仅替换 @(\w+) 为安全 标签),再转 template.HTML;
示例安全解析函数(简化版):
func renderMentions(text string) template.HTML {
re := regexp.MustCompile(`@(\w+)`)
safeHTML := re.ReplaceAllStringFunc(text, func(match string) string {
user := re.FindStringSubmatch([]byte(match))[1:] // 提取用户名
if len(user) > 0 && isValidUsername(string(user)) { // 白名单校验
return fmt.Sprintf(`<a class="tweet-url username" href="/user/%s" data-screen-name="%s" rel="nofollow">%s</a>`,
template.HTMLEscapeString(string(user)),
template.HTMLEscapeString(string(user)),
template.HTMLEscapeString(string(user)))
}
return match // 无效则保留原文本
})
return template.HTML(safeHTML)
}
总之,template.HTML 是必要且高效的解决方案,但其安全性完全取决于上游处理——永远不要将未经验证的用户输入直接包裹其中。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










