在本地开发中,我们经常会遇到一些在本地环境中运行正常,但在生产环境中却出现问题的情况。最近,我在使用 Golang 1.22 进行网页开发时,就遇到了这样一个问题。
问题描述
在我的项目中,我使用了 Golang 的 html/template 包来生成网页。在网页的 JavaScript 部分,我使用了模板字面量来嵌入 Golang 的模板变量,如下所示:
<script>
let link = `/post/?id={{ .id }}`;
</script>
在本地编译和测试运行时,一切正常。然而,当我通过 CI/CD 自动化流程部署到生产环境后,网页却出现了空白页面。查看错误日志后发现,错误信息为:
{{.id}} appears in a JS template literal
原因分析
经过排查,我发现问题出在 Golang 的版本差异上。本地环境使用的是 Golang 1.22,而 CI/CD 流程中使用的是 Golang 1.20。
Golang 1.20 在处理 JS 模板字面量中的 Golang 模板变量时,即在 JS 的反引号字符串中使用 Go 的模板变量,会报错{{.id}} appears in a JS template literal
。
在 Golang 1.20 中,JS 模板字面量中的 Golang 模板变量会被视为非法操作,在 Golang 1.20 版本之前,模板解析器没有正确处理 JavaScript 模板字面量中的反引号(backticks),这可能导致未预期的代码注入风险,如果用户输入的内容包含恶意代码,这些代码可能会被直接插入到生成的 JavaScript 中,从而导致跨站脚本(XSS)攻击。
具体来说,模板字符串中的变量插值(如 ${…})可能会导致意外的上下文切换,从而引发安全漏洞或错误行为。当遇到 ${
后,如果包含特定字符(如"
, '
, $
, {
, }
),Go 1.20 会认为这是一个复杂的表达式,并拒绝解析。为了提高安全性,Golang 团队决定在 1.20 版本中禁用在 JavaScript 模板字面量中使用 Go 模板动作,因此会报错。
然而,在 Golang 1.22 中,这一限制被移除了,允许在 JS 模板字面量中使用 Golang 模板变量。这是因为 Golang 1.22 对模板解析器进行了改进,允许在 JS 模板字面量中安全地嵌入 Golang 模板变量 。
因此,在本地开发环境中没有遇到问题,但在 CI/CD 环境中由于使用了较旧的 Golang 版本,导致了上述错误。
参考链接:
- GitHub Issue: html/template: refuse to parse complex JS template literals
- GitHub Issue: html/template: allow actions in JS template literals
- Go 1.22 Release Notes
解决方法
遇到这种问题,可以有以下几种解决方法:
升级 Golang 版本:确保 CI/CD 流程中使用的 Golang 版本与本地开发环境一致。可以在 CI/CD 配置文件中指定使用 Golang 1.22 版本。
修改模板代码:如果无法升级 Golang 版本,可以考虑修改模板代码,避免在 JS 模板字面量中直接使用 Golang 模板变量。例如,可以先声明定义一个变量来存储变量值,然后使用 JS 模板字面量读取该值,或者直接使用
+
拼接。<script> let id = {{ .id }}; let link = `/posts/?id=${id}`; // 或者 let link = `/posts/?id=` + {{ .id }}; </script> // 在修改后的代码中,我们没有在将 JavaScirpt 的模板字面量中使用 Go 模板变量 `{{ .id }}` // 从而避免了 Golang 1.20 版本中的错误。
如果你必须在 Golang 1.20 版本中使用这种语法,可以通过设置环境变量
GODEBUG=jstmpllitinterp=1
来临时解决这个问题,但需要注意这样做可能会带来安全风险。
总结
在开发过程中,版本差异可能会导致一些意想不到的问题。通过了解不同版本之间的差异,并采取相应的解决方法,可以有效避免这些问题的发生。希望这篇文章能对遇到类似问题的开发者有所帮助。