Golang中[]uint8在HTML模板中打印出现异常问题
Golang中[]uint8在HTML模板中打印出现异常问题
我尝试在HTML模板的<script></script>块中打印一个[]uint8切片,但它出现了奇怪的行为。我这么说是因为打印[]int8的表现符合我的预期。
如果上面的Playground链接失效,代码是:
package main
import (
"html/template"
"os"
)
func main() {
Create := func(name, t string) *template.Template {
return template.Must(template.New(name).Parse(t))
}
t2 := Create("t2", "outside script:\n{{.INT8_ARR}}\n{{.UINT8_ARR}}\n{{.INT8}}\n{{.UINT8}}\n<script>\n{{.INT8_ARR}}\n{{.UINT8_ARR}}\n{{.INT8}}\n{{.UINT8}}\n</script>")
t2.Execute(os.Stdout, struct {
INT8_ARR []int8
UINT8_ARR []uint8
INT8 int8
UINT8 uint8
}{
INT8_ARR: []int8{2, 3, 4},
UINT8_ARR: []uint8{2, 3, 4},
INT8: 5,
UINT8: 6,
})
}
输出结果是:
outside script:
[2 3 4]
[2 3 4]
5
6
<script>
[2,3,4]
"AgME"
5
6
</script>
出于某种原因,它似乎对uint8数组进行了base64编码(即"AgME")。有人知道这是怎么回事吗?是不是html/template认为这是一个字节数组并试图对其进行清理?
更多关于Golang中[]uint8在HTML模板中打印出现异常问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
在调试器中运行了模板代码,并找到了一个更简短的示例来展示同样的问题。
package main
import (
"encoding/json"
"fmt"
)
func main() {
v, _ := json.Marshal([]int8{3, 4, 5})
fmt.Println(string(v))
v, _ = json.Marshal([]uint8{3, 4, 5})
fmt.Println(string(v))
}
这将打印:
[3,4,5]
"AwQF"
深入研究后,我发现了:src/encoding/json/encode.go - go - Git at Google
看来我是对的。代码似乎将字节切片与 uint8 切片混淆了。
规范指出 byte 只是 uint8 的别名(依据:The Go Programming Language Specification - The Go Programming Language)。所以 uint8 与 json.Encoder 配合得不太好。
更多关于Golang中[]uint8在HTML模板中打印出现异常问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这是 html/template 包对 []uint8(即 []byte)类型的特殊处理。在 HTML 模板中,[]uint8 被识别为字节切片,在 <script> 标签内会自动进行 JavaScript 字符串转义,导致显示为 base64 编码的字符串。
示例代码演示了这个问题:
package main
import (
"html/template"
"os"
)
func main() {
tmpl := `Outside script: {{.ByteSlice}}
<script>
console.log({{.ByteSlice}});
console.log({{.Uint8Slice}});
</script>`
t := template.Must(template.New("test").Parse(tmpl))
data := struct {
ByteSlice []byte
Uint8Slice []uint8
}{
ByteSlice: []byte{72, 101, 108, 108, 111}, // "Hello" 的 ASCII 码
Uint8Slice: []uint8{72, 101, 108, 108, 111},
}
t.Execute(os.Stdout, data)
}
输出:
Outside script: [72 101 108 108 111]
<script>
console.log("SGVsbG8=");
console.log("SGVsbG8=");
</script>
要避免这个问题,可以将 []uint8 转换为其他类型。以下是几种解决方案:
- 转换为字符串:
package main
import (
"html/template"
"os"
)
func main() {
tmpl := `<script>
console.log({{.AsString}});
</script>`
t := template.Must(template.New("test").Parse(tmpl))
byteSlice := []byte{72, 101, 108, 108, 111}
data := struct {
AsString string
}{
AsString: string(byteSlice),
}
t.Execute(os.Stdout, data)
}
- 使用自定义函数:
package main
import (
"html/template"
"os"
)
func main() {
funcMap := template.FuncMap{
"toJSArray": func(b []byte) template.JS {
str := "["
for i, v := range b {
if i > 0 {
str += ","
}
str += string('0' + v/10)
str += string('0' + v%10)
}
str += "]"
return template.JS(str)
},
}
tmpl := `<script>
console.log({{.ByteSlice | toJSArray}});
</script>`
t := template.Must(template.New("test").Funcs(funcMap).Parse(tmpl))
data := struct {
ByteSlice []byte
}{
ByteSlice: []byte{2, 3, 4},
}
t.Execute(os.Stdout, data)
}
- 使用
template.JS类型:
package main
import (
"html/template"
"os"
)
func main() {
tmpl := `<script>
console.log({{.AsJS}});
</script>`
t := template.Must(template.New("test").Parse(tmpl))
byteSlice := []byte{2, 3, 4}
jsArray := "["
for i, v := range byteSlice {
if i > 0 {
jsArray += ","
}
jsArray += string('0' + v)
}
jsArray += "]"
data := struct {
AsJS template.JS
}{
AsJS: template.JS(jsArray),
}
t.Execute(os.Stdout, data)
}
html/template 包对 <script> 标签内的内容进行自动转义是为了防止 XSS 攻击。[]uint8 被当作原始字节数据处理,因此会进行 base64 编码以确保安全。

