- Go Gin 模板渲染遇到 Json 转义反斜杠问题 使用 Golang 及其 Web 框架 Gin,结合 template 模板以及 htmx 能够很高效地渲染 html 网页及展示给用户。因为 htmx 的特性可以让后台直接返回 html 片段,所以对于个人开发者及偏后台的研发团队来说是一个非常高效的组合。能够快速地开发 MVP 并部署到生产服务器上。而且生成纯 html 的形式展示给用户也有利于 SEO 以及使用 CDN 加速。
Golang 在渲染模板时做了一些数据安全方面的处理,所以如果是通过模板的方式直接在 html 文件中嵌入一些 html 代码是会被转义的。但 Golang 的 template 库提供了一个代码安全的处理方法:就是把 html 代码以 template.HTML 的类型传递给 html 模板。这样处理后嵌入的 html 代码就会被视为安全代码,正常组合成一个完整的 html 文件。
例如:
router.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", "<h1>开放数据接口API</h1>")
})
在 html 模板中使用 {{ . }} 就可以把以上的 h1 嵌入到 html 中。
但最近为了达到优化 SEO 的目的,给网站进行升级优化,加入 JsonLD 信息时,就发现 template.HTML 并不能完美地处理 Json 对象,总是会出现反斜杠把双引号转义了。
<script type="application/ld+json">"{\"@context\":\"https://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"首页\",\"item\":\"https://spiderbuf.cn\"}]}"</script>
解决方法
这个格式的 JsonLD 信息显然是无效的,反复确认相关函数返回的是 template.HTML 类型后就意识到,template.HTML 是无法完美处理 Json 的。于是又开始了一系列的探索,最终还是从 Golang 的 template 库中找到了方法:template.JS 。
template.JS 也是一个自定义的数据类型,与 template.HTML 类似,也是为了代码安全而设置的数据类型,但 template.HTML 针对的是 html 标签相关的内容,而 template.JS 是针对 Json 的。
下面一起来对比一个问题解决前及解决后的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="application/ld+json">{{ . }}</script>
</head>
<body>
<div>Python爬虫开发</div>
</body>
</html>
html 的代码对这个问题没有特别影响,所以解决 Gin Json 双引号被反斜杠转义的问题不需要改造 html 模板代码。
// 这是出问题的代码
func genJson() template.HTML {
...//自定义业务逻辑
json_data , err := json.Marshal(pre_json)
if err != nil {
log.Printf("JSON Marshal failed: %v", err)
}
return template.HTML(string(json_data))
}
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")
router.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", genJson())
})
router.Run(":8080")
}
以上是尝试使用 template.HTML 来解决 Json 被转义问题的代码,但没有成功。要想解决这个问题,就把 template.HTML 改成 template.JS 即可。
改造后能正常在 html 模板中嵌入合法的 Json 字符串的代码:
func genJson() template.JS {// 注意这里
...//自定义业务逻辑
json_data , err := json.Marshal(pre_json)
if err != nil {
log.Printf("JSON Marshal failed: %v", err)
}
return template.JS(string(json_data)) // 注意这里
}
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")
router.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", genJson())
})
router.Run(":8080")
}
知道了方法就很简单,不知道方法的时候急死人。
什么是 JsonLD
顺便科普一下什么是 JsonLD:
JSON-LD(JSON Linked Data) 是一种基于 JSON 的轻量级数据格式,专门用于表示结构化关联数据(Linked Data)。它是 W3C 推荐的语义网标准之一,旨在通过 JSON 的简洁性实现数据互联和机器可读性。能够进行 SEO 优化,被 Google、Bing 等搜索引擎支持,用于增强网页内容理解。