我最近交换了数据存储区,因为副作用必须将struct.
HTML中的struct字段更改为字符串以与marshaller / DB驱动程序兼容.此字段RenderedDesc包含通过
russross/blackfriday传递的呈现HTML.
以前我可以将整个结构“按原样”传递给模板,并在模板中调用{{.RenderedDesc}}.
因为它现在是一个字符串,我添加了一个过滤器来将其转换回模板渲染:
templates.go
func RenderUnsafe(s string) template.HTML {
return template.HTML(s)
}
template.FuncMap{
...
"unsafe": RenderUnsafe,
}
_content.tmpl
...
<div class="detail">
{{ .RenderedDesc | unsafe }}
</div>
...
有没有更好的方法来实现这一点,而无需在模板级别使用过滤器?如果没有从我的数据库驱动程序(而不是卡片)重写编组逻辑,看起来这是“存储”字符串但渲染原始HTML的最简单方法.
最佳答案 恕我直言,正确的方法是使用过滤器,就像你已经在做的那样.有更多的方法可以实现相同的,其中一种方法是使用标签并将结构转换为map [string] Interface {}.由于可以使用与结构相同的方式到达地图字段,因此模板将保持不变.
给我看代码(playground):
package main
import (
"html/template"
"os"
"reflect"
)
var templates = template.Must(template.New("tmp").Parse(`
<html>
<head>
</head>
<body>
<h1>Hello</h1>
<div class="content">
Usafe Content = {{.Content}}
Safe Content = {{.Safe}}
Bool = {{.Bool}}
Num = {{.Num}}
Nested.Num = {{.Nested.Num}}
Nested.Bool = {{.Nested.Bool}}
</div>
</body>
</html>
`))
func asUnsafeMap(any interface{}) map[string]interface{} {
v := reflect.ValueOf(any)
if v.Kind() != reflect.Struct {
panic("asUnsafeMap invoked with a non struct parameter")
}
m := map[string]interface{}{}
for i := 0; i < v.NumField(); i++ {
value := v.Field(i)
if !value.CanInterface() {
continue
}
ftype := v.Type().Field(i)
if ftype.Tag.Get("unsafe") == "html" {
m[ftype.Name] = template.HTML(value.String())
} else {
m[ftype.Name] = value.Interface()
}
}
return m
}
func main() {
templates.ExecuteTemplate(os.Stdout, "tmp", asUnsafeMap(struct {
Content string `unsafe:"html"`
Safe string
Bool bool
Num int
Nested struct {
Num int
Bool bool
}
}{
Content: "<h2>Lol</h2>",
Safe: "<h2>Lol</h2>",
Bool: true,
Num: 10,
Nested: struct {
Num int
Bool bool
}{
Num: 9,
Bool: true,
},
}))
}
输出:
<html>
<head>
</head>
<body>
<h1>Hello</h1>
<div class="content">
Usafe Content = <h2>Lol</h2>
Safe Content = <h2>Lol</h2>
Bool = true
Num = 10
Nested.Num = 9
Nested.Bool = true
</div>
</body>
</html>
注意:前面的代码不适用于嵌套结构,但很容易添加对它们的支持.此外,标记为不安全的每个字段都将被视为字符串.