2014-01-29 73 views
3

我最近換出了數據存儲區,並且作爲副作用必須將結構字段從template.HTML更改爲string以與編組器/ DB驅動程序兼容。該字段RenderedDesc包含通過russross/blackfriday傳遞的呈現的HTML。將template.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

因爲template.HTML的目的是包裝已知安全標記,所以您的不安全過濾器應該被稱爲「安全」。在你的情況下,除非你需要在不同的領域做很多事情,否則我只需在結構體中定義一個方法,將你的字段作爲template.HTML值返回。 – krait

+0

@krait良好的捕獲;我原本稱之爲「安全」,但不清楚如何解釋這一點。 RE:在結構上定義一個方法:爲了在模板中使用結果字段,我還必須將它傳遞給我的模板,類似'M {renderedField:value}',並將{{.RenderedDesc }}'帶有新字段/ break {{with .Listing}}分成兩部分。 – elithrar

回答

1

恕我直言,正確的做法是使用過濾器,就像你已經在做的那樣。有更多的方法來實現相同的,其中之一是使用標籤並將結構轉換爲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> 

注:前面的代碼不與嵌套結構的工作,但它會很容易添加支持爲他們。此外,標記爲不安全的每個字段都將被視爲字符串。