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

时间:2014-01-29 14:25:42

标签: go

我最近换出了数据存储区,因为副作用必须将结构字段从template.HTML更改为string以与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的最简单方法。

1 个答案:

答案 0 :(得分:2)

恕我直言,正确的方法是使用过滤器,就像你已经在做的那样。有更多方法可以实现相同的目标,其中之一是使用标签并将结构转换为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>

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