go – 将template.HTML直接渲染到模板中

我最近交换了数据存储区,因为副作用必须将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  = &lt;h2&gt;Lol&lt;/h2&gt;
            Bool          = true
            Num           = 10
            Nested.Num    = 9
            Nested.Bool   = true
        </div>
    </body>
</html>

注意:前面的代码不适用于嵌套结构,但很容易添加对它们的支持.此外,标记为不安全的每个字段都将被视为字符串.

点赞