日照电工 發表於 2020-3-12 19:50:00

Go标准库之html/template

<p class="md-focus-p"><span class="md-line md-end-block md-focus"><span class="md-expand">html/template<span><code>包实现了数据驱动的模板,用于生成可防止代码注入的安全的HTML内容。它提供了和</code><span>text/template<span><code>包相同的接口,Go语言中输出HTML的场景都应使用</code><span>html/template`这个包。</span></span></span></span></span></span></p>
<h3 class="md-end-block md-heading"><span>一、模板与渲染</span></h3>
<p><span class="md-line md-end-block"><span>在一些前后端不分离的Web架构中,我们通常需要在后端将一些数据渲染到HTML文档中,从而实现动态的网页(网页的布局和样式大致一样,但展示的内容并不一样)效果。</span></span></p>
<p><span class="md-line md-end-block"><span>我们这里说的模板可以理解为事先定义好的HTML文档文件,模板渲染的作用机制可以简单理解为文本替换操作–使用相应的数据去替换HTML文档中事先准备好的标记。</span></span></p>
<p><span class="md-line md-end-block"><span>很多编程语言的Web框架中都使用各种模板引擎,比如Python语言中Flask框架中使用的jinja2模板引擎。</span></span></p>
<h3 class="md-end-block md-heading"><span>二、Go语言的模板引擎</span></h3>
<p><span class="md-line md-end-block"><span>Go语言内置了文本模板引擎<span><code>text/template</code><span>和用于HTML文档的<span><code>html/template</code><span>。它们的作用机制可以简单归纳如下:</span></span></span></span></span></span></p>
<ol class="ol-list">
<li class="md-list-item">
<p><span class="md-line md-end-block"><span>模板文件通常定义为<span><code>.tmpl</code><span>和<span><code>.tpl</code><span>为后缀(也可以使用其他的后缀),必须使用<span><code>UTF8</code><span>编码。</span></span></span></span></span></span></span></span></p>
</li>
<li class="md-list-item">
<p><span class="md-line md-end-block"><span>模板文件中使用<span><code>{{</code><span>和<span><code>}}</code><span>包裹和标识需要传入的数据。</span></span></span></span></span></span></p>
</li>
<li class="md-list-item">
<p><span class="md-line md-end-block"><span>传给模板这样的数据就可以通过点号(<span><code>.</code><span>)来访问,如果数据是复杂类型的数据,可以通过{ { .FieldName }}来访问它的字段。</span></span></span></span></p>
</li>
<li class="md-list-item">
<p><span class="md-line md-end-block"><span>除<span><code>{{</code><span>和<span><code>}}</code><span>包裹的内容外,其他内容均不做修改原样输出。</span></span></span></span></span></span></p>
</li>
</ol>
<h3 class="md-end-block md-heading"><span>三、模板引擎的使用</span></h3>
<p><span class="md-line md-end-block"><span>Go语言模板引擎的使用可以分为三部分:定义模板文件、解析模板文件和模板渲染.</span></span></p>
<h4 class="md-end-block md-heading"><span>1. 定义模板文件</span></h4>
<p><span class="md-line md-end-block"><span>其中,定义模板文件时需要我们按照相关语法规则去编写,后文会详细介绍。</span></span></p>
<h4 class="md-end-block md-heading"><span>2. 解析模板文件</span></h4>
<p><span class="md-line md-end-block"><span>上面定义好了模板文件之后,可以使用下面的常用方法去解析模板文件,得到模板对象:</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">func (t *Template) Parse(src string) (*Template, error)
func ParseFiles(filenames ...string) (*Template, error)
func ParseGlob(pattern string) (*Template, error)</pre>
</div>
<p><span class="md-line md-end-block"><span>当然,你也可以使用<span><code>func New(name string) *Template</code><span>函数创建一个名为<span><code>name</code><span>的模板,然后对其调用上面的方法去解析模板字符串或模板文件。</span></span></span></span></span></span></p>
<h4 class="md-end-block md-heading md-focus"><span class="md-expand">3. 模板渲染</span></h4>
<p><span class="md-line md-end-block"><span>渲染模板简单来说就是使用数据去填充模板,当然实际上可能会复杂很多。</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">func (t *Template) Execute(wr io.Writer, data interface{}) error
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error</pre>
</div>
<h3 class="md-end-block md-heading"><span>4. 基本示例</span></h3>
<h4 class="md-end-block md-heading"><span>4.1 定义模板文件</span></h4>
<p><span class="md-line md-end-block"><span>我们按照Go模板语法定义一个<span><code>hello.tmpl</code><span>的模板文件,内容如下:</span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">&lt;!doctype html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="ie=edge"&gt;
    &lt;title&gt;Hello&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;Hello {{ . }}&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
</div>
<h4 class="md-end-block md-heading"><span>4.2 解析和渲染模板文件</span></h4>
<p><span class="md-line md-end-block"><span>然后我们创建一个<span><code>main.go</code><span>文件,在其中写下HTTP server端代码如下:</span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main

import (
    "html/template"
    "log"
    "net/http"
)

func sayHello(w http.ResponseWriter, r *http.Request){
    // 解析模板
    t,err := template.ParseFiles("./hello.tmpl")
    if err!=nil {
      log.Println("Parse template failed, err%v", err)
      return
    }
    // 渲染模板
    name := "飞哥哥"
    err = t.Execute(w, name)
    if err!=nil {
      log.Println("render template failed, err%v", err)
      return
    }
}

func main() {
    http.HandleFunc("/", sayHello)
    err := http.ListenAndServe(":9000", nil)
    if err!=nil {
      log.Println("http server start failed,err:%v",err)
    }
}</pre>
</div>
<p><span class="md-line md-end-block"><span>将上面的<span><code>main.go</code><span>文件编译执行,然后使用浏览器访问<span><code>http://127.0.0.1:9000</code><span>就能看到页面上显示了“Hello 沙河小王子”。 这就是一个最简单的模板渲染的示例,Go语言模板引擎详细用法请往下阅读。</span></span></span></span></span></span></p>
<h3 class="md-end-block md-heading"><span>四、模板语法</span></h3>
<h4 class="md-end-block md-heading"><span>1. {{.}}</span></h4>
<blockquote>
<p><span class="md-line md-end-block"><span>模板语法都包含在<span><code>{{</code><span>和<span><code>}}</code><span>中间,其中<span><code>{{.}}</code><span>中的点表示当前对象。当我们传入一个结构体对象时,我们可以根据<span><code>.</code><span>来访问结构体的对应字段。例如:</span></span></span></span></span></span></span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">package main

import (
    "html/template"
    "log"
    "net/http"
)

type User struct {
    Name string
    Gender string
    Age int
}

func sayHello(w http.ResponseWriter, r *http.Request){
    // 解析模板
    t,err := template.ParseFiles("./hello.tmpl")
    if err!=nil {
      log.Println("Parse template failed, err%v", err)
      return
    }
    // 渲染模板
    // 渲染字符串
    name := "飞哥哥"
    //err = t.Execute(w, name)
    // 渲染结构体
    user := User{
      Name:name,
      Gender:"男",
      Age:23,
    }
    err = t.Execute(w, user)
    if err!=nil {
      log.Println("render template failed, err%v", err)
      return
    }
}

func main() {
    http.HandleFunc("/", sayHello)
    err := http.ListenAndServe(":9000", nil)
    if err!=nil {
      log.Println("http server start failed,err:%v",err)
    }
}</pre>
</div>
<p><span class="md-line md-end-block"><span>模板文件<span><code>hello.tmpl</code><span>内容如下:</span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="ie=edge"&gt;
    &lt;title&gt;Hello&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;p&gt;Hello {{.Name}}&lt;/p&gt;
    &lt;p&gt;性别:{{.Gender}}&lt;/p&gt;
    &lt;p&gt;年龄:{{.Name}}&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;  </pre>
</div>
<p><span class="md-line md-end-block"><span>渲染map类型</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">func sayHello(w http.ResponseWriter, r *http.Request){
    // 解析模板
    t,err := template.ParseFiles("./hello.tmpl")
    if err!=nil {
      log.Println("Parse template failed, err%v", err)
      return
    }
    // 渲染模板
    // 渲染字符串
    name := "飞哥哥"
    //err = t.Execute(w, name)
    // 渲染结构体
    user := User{
      Name:name,
      Gender:"男",
      Age:23,
    }
    //err = t.Execute(w, user)
    // 渲染map
    m := mapinterface{}{
      "name": name,
      "gender": "男",
      "age": 24,
    }
    //err = t.Execute(w, m)
    hobbyList := []string{
      "篮球",
      "足球",
      "双色球",
    }

    err = t.Execute(w, mapinterface{}{
      "m": m,
      "user": user,
      "hobby": hobbyList,
    })
    if err!=nil {
      log.Println("render template failed, err%v", err)
      return
    }
}</pre>
</div>
<p><span class="md-line md-end-block"><span>hello.tmpl</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">&lt;p&gt;Hello {{ .user.Name }}&lt;/p&gt;
&lt;p&gt;年龄 {{ .user.Age }}&lt;/p&gt;
&lt;p&gt;性别 {{ .user.Gender }}&lt;/p&gt;

&lt;p&gt;Hello {{ .m.name }}&lt;/p&gt;
&lt;p&gt;年龄 {{ .m.age }}&lt;/p&gt;
&lt;p&gt;性别 {{ .m.gender }}&lt;/p&gt;

&lt;hr&gt;
{{ range $idx, $hobby := .hobby}}
&lt;p&gt;{{$idx}} - {{$hobby}}&lt;/p&gt;</pre>
</div>
</blockquote>
<h4 class="md-end-block md-heading"><span>2. 注释</span></h4>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">{{/* a comment */}}
注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止。</pre>
</div>
<h4 class="md-end-block md-heading"><span>3. pipeline</span></h4>
<p><span class="md-line md-end-block"><span><code>&nbsp; pipeline</code><span>是指产生数据的操作。比如<span><code>{{.}}</code><span>、<span><code>{{.Name}}</code><span>等。Go的模板语法中支持使用管道符号<span><code>|</code><span>链接多个命令,用法和unix下的管道类似:<span><code>|</code><span>前面的命令会将运算结果(或返回值)传递给后一个命令的最后一个位置。</span></span></span></span></span></span></span></span></span></span></span></p>
<p><span class="md-line md-end-block"><span><strong>注意:</strong><span>并不是只有使用了<span><code>|</code><span>才是pipeline。Go的模板语法中,<span><code>pipeline的</code><span>概念是传递数据,只要能产生数据的,都是<span><code>pipeline</code><span>。</span></span></span></span></span></span></span></span></span></p>
<h4 class="md-end-block md-heading"><span>4. 变量</span></h4>
<p><span class="md-line md-end-block"><span>&nbsp; &nbsp; 我们还可以在模板中声明变量,用来保存传入模板的数据或其他语句生成的结果。具体语法如下:</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">$obj := {{.}}</pre>
</div>
<p><span class="md-line md-end-block"><span>其中<span><code>$obj</code><span>是变量的名字,在后续的代码中就可以使用该变量了。</span></span></span></span></p>
<h4 class="md-end-block md-heading"><span>5. 移除空格</span></h4>
<p><span class="md-line md-end-block"><span>有时候我们在使用模板语法的时候会不可避免的引入一下空格或者换行符,这样模板最终渲染出来的内容可能就和我们想的不一样,这个时候可以使用<span><code>{{-</code><span>语法去除模板内容左侧的所有空白符号, 使用<span><code>-}}</code><span>去除模板内容右侧的所有空白符号。</span></span></span></span></span></span></p>
<p><span class="md-line md-end-block"><span>例如:</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">{{- .Name -}}</pre>
</div>
<p><span class="md-line md-end-block"><span><strong>注意:</strong><span><code>-</code><span>要紧挨<span><code>{{</code><span>和<span><code>}}</code><span>,同时与模板值之间需要使用空格分隔。</span></span></span></span></span></span></span></span></p>
<h4 class="md-end-block md-heading"><span>6. 条件判断</span></h4>
<p><span class="md-line md-end-block"><span>Go模板语法中的条件判断有以下几种:</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">{{if pipeline}} T1 {{end}}
{{if pipeline}} T1 {{else}} T0 {{end}}
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}</pre>
</div>
<h4 class="md-end-block md-heading"><span>7. range</span></h4>
<p><span class="md-line md-end-block"><span>Go的模板语法中使用<span><code>range</code><span>关键字进行遍历,有以下两种写法,其中<span><code>pipeline</code><span>的值必须是数组、切片、字典或者通道。</span></span></span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">{{range pipeline}} T1 {{end}}
如果pipeline的值其长度为0,不会有任何输出

{{range pipeline}} T1 {{else}} T0 {{end}}
如果pipeline的值其长度为0,则会执行T0。</pre>
</div>
<h4 class="md-end-block md-heading"><span>8. with</span></h4>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">{{with pipeline}} T1 {{end}}
如果pipeline为empty不产生输出,否则将dot设为pipeline的值并执行T1。不修改外面的dot。

{{with pipeline}} T1 {{else}} T0 {{end}}
如果pipeline为empty,不改变dot并执行T0,否则dot设为pipeline的值并执行T1。</pre>
</div>
<h4 class="md-end-block md-heading"><span>9. 预定义函数</span></h4>
<p><span class="md-line md-end-block"><span>执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一般不在模板内定义函数,而是使用Funcs方法添加函数到模板里。</span></span></p>
<p><span class="md-line md-end-block"><span>预定义的全局函数如下:</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">and
    函数返回它的第一个empty参数或者最后一个参数;
    就是说"and x y"等价于"if x then y else x";所有参数都会执行;
or
    返回第一个非empty参数或者最后一个参数;
    亦即"or x y"等价于"if x then x else y";所有参数都会执行;
not
    返回它的单个参数的布尔值的否定
len
    返回它的参数的整数类型长度
index
    执行结果为第一个参数以剩下的参数为索引/键指向的值;
    如"index x 1 2 3"返回x的值;每个被索引的主体必须是数组、切片或者字典。
print
    即fmt.Sprint
printf
    即fmt.Sprintf
println
    即fmt.Sprintln
html
    返回与其参数的文本表示形式等效的转义HTML。
    这个函数在html/template中不可用。
urlquery
    以适合嵌入到网址查询中的形式返回其参数的文本表示的转义值。
    这个函数在html/template中不可用。
js
    返回与其参数的文本表示形式等效的转义JavaScript。
call
    执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;
    如"call .X.Y 1 2"等价于go语言里的dot.X.Y(1, 2);
    其中Y是函数类型的字段或者字典的值,或者其他类似情况;
    call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);
    该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型;
    如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;</pre>
</div>
<h4 class="md-end-block md-heading"><span>10. 比较函数</span></h4>
<p><span class="md-line md-end-block"><span>布尔函数会将任何类型的零值视为假,其余视为真。</span></span></p>
<p><span class="md-line md-end-block"><span>下面是定义为函数的二元比较运算的集合:</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">eq      如果arg1 == arg2则返回真
ne      如果arg1 != arg2则返回真
lt      如果arg1 &lt; arg2则返回真
le      如果arg1 &lt;= arg2则返回真
gt      如果arg1 &gt; arg2则返回真
ge      如果arg1 &gt;= arg2则返回真</pre>
</div>
<p><span class="md-line md-end-block"><span>为了简化多参数相等检测,eq(只有eq)可以接受2个或更多个参数,它会将第一个参数和其余参数依次比较,返回下式的结果:</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">{{eq arg1 arg2 arg3}}</pre>
</div>
<p><span class="md-line md-end-block"><span>比较函数只适用于基本类型(或重定义的基本类型,如”type Celsius float32”)。但是,整数和浮点数不能互相比较。</span></span></p>
<h4 class="md-end-block md-heading"><span>11. 自定义函数</span></h4>
<p><span class="md-line md-end-block"><span>Go的模板支持自定义函数。</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">func sayHello(w http.ResponseWriter, r *http.Request) {
    htmlByte, err := ioutil.ReadFile("./hello.tmpl")
    if err != nil {
      fmt.Println("read html failed, err:", err)
      return
    }
    // 自定义一个夸人的模板函数
    kua := func(arg string) (string, error) {
      return arg + "真帅", nil
    }
    // 采用链式操作在Parse之前调用Funcs添加自定义的kua函数
    tmpl, err := template.New("hello").Funcs(template.FuncMap{"kua": kua}).Parse(string(htmlByte))
    if err != nil {
      fmt.Println("create template failed, err:", err)
      return
    }

    user := UserInfo{
      Name:   "小王子",
      Gender: "男",
      Age:    18,
    }
    // 使用user渲染模板,并将结果写入w
    tmpl.Execute(w, user)
}</pre>
</div>
<p><span class="md-line md-end-block"><span>我们可以在模板文件<span><code>hello.tmpl</code><span>中按照如下方式使用我们自定义的<span><code>kua</code><span>函数了。</span></span></span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">{{kua .Name}}</pre>
</div>
<h4 class="md-end-block md-heading"><span>12. 嵌套template</span></h4>
<p><span class="md-line md-end-block"><span>我们可以在template中嵌套其他的template。这个template可以是单独的文件,也可以是通过<span><code>define</code><span>定义的template。</span></span></span></span></p>
<p><span class="md-line md-end-block"><span>举个例子: <span><code>t.tmpl</code><span>文件内容如下:</span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="ie=edge"&gt;
    &lt;title&gt;tmpl test&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
   
    &lt;h1&gt;测试嵌套template语法&lt;/h1&gt;
    &lt;hr&gt;
    {{template "ul.tmpl"}}
    &lt;hr&gt;
    {{template "ol.tmpl"}}
&lt;/body&gt;
&lt;/html&gt;

{{ define "ol.tmpl"}}
&lt;ol&gt;
    &lt;li&gt;吃饭&lt;/li&gt;
    &lt;li&gt;睡觉&lt;/li&gt;
    &lt;li&gt;打豆豆&lt;/li&gt;
&lt;/ol&gt;
{{end}}
</pre>
</div>
<p>  </p>
<p><span class="md-line md-end-block"><span><code>ul.tmpl</code><span>文件内容如下:</span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">&lt;ul&gt;
    &lt;li&gt;注释&lt;/li&gt;
    &lt;li&gt;日志&lt;/li&gt;
    &lt;li&gt;测试&lt;/li&gt;
&lt;/ul&gt;</pre>
</div>
<p><span class="md-line md-end-block"><span>我们注册一个<span><code>templDemo</code><span>路由处理函数.</span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">http.HandleFunc("/tmpl", tmplDemo)</pre>
</div>
<p><span class="md-line md-end-block"><span><code>tmplDemo</code><span>函数的具体内容如下:</span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">func tmplDemo(w http.ResponseWriter, r *http.Request) {
    tmpl, err := template.ParseFiles("./t.tmpl", "./ul.tmpl")
    if err != nil {
      fmt.Println("create template failed, err:", err)
      return
    }
    user := UserInfo{
      Name:   "小王子",
      Gender: "男",
      Age:    18,
    }
    tmpl.Execute(w, user)
}</pre>
</div>
<p><span class="md-line md-end-block"><span><strong>注意</strong><span>:在解析模板时,被嵌套的模板一定要在后面解析,例如上面的示例中<span><code>t.tmpl</code><span>模板中嵌套了<span><code>ul.tmpl</code><span>,所以<span><code>ul.tmpl</code><span>要在<span><code>t.tmpl</code><span>后进行解析。</span></span></span></span></span></span></span></span></span></span></span></p>
<h4 class="md-end-block md-heading"><span>13. block</span></h4>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">{{block "name" pipeline}} T1 {{end}}</pre>
</div>
<p><span class="md-line md-end-block"><span><code>block</code><span>是定义模板<span><code>{{define "name"}} T1 {{end}}</code><span>和执行<span><code>{{template "name" pipeline}}</code><span>缩写,典型的用法是定义一组根模板,然后通过在其中重新定义块模板进行自定义。</span></span></span></span></span></span></span></p>
<p><span class="md-line md-end-block"><span>定义一个根模板<span><code>templates/base.tmpl</code><span>,内容如下:</span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;title&gt;Go Templates&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="container-fluid"&gt;
    {{block "content" . }}{{end}}
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
</div>
<p><span class="md-line md-end-block"><span>然后定义一个<span><code>templates/index.tmpl</code><span>,”继承”<span><code>base.tmpl</code><span>:</span></span></span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">{{template "base.tmpl" .}}

{{define "content"}}
    &lt;div&gt;Hello world!&lt;/div&gt;
{{end}}</pre>
</div>
<p><span class="md-line md-end-block"><span>然后使用<span><code>template.ParseGlob</code><span>按照正则匹配规则解析模板文件,然后通过<span><code>ExecuteTemplate</code><span>渲染指定的模板:</span></span></span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">func index(w http.ResponseWriter, r *http.Request){
    tmpl, err := template.ParseGlob("templates/*.tmpl")
    if err != nil {
      fmt.Println("create template failed, err:", err)
      return
    }
    err = tmpl.ExecuteTemplate(w, "index.tmpl", nil)
    if err != nil {
      fmt.Println("render template failed, err:", err)
      return
    }
}</pre>
</div>
<p><span class="md-line md-end-block"><span>如果我们的模板名称冲突了,例如不同业务线下都定义了一个<span><code>index.tmpl</code><span>模板,我们可以通过下面两种方法来解决。</span></span></span></span></p>
<ol class="ol-list">
<li class="md-list-item">
<p><span class="md-line md-end-block"><span>在模板文件开头使用<span><code>{{define 模板名}}</code><span>语句显式的为模板命名。</span></span></span></span></p>
</li>
<li class="md-list-item">
<p><span class="md-line md-end-block"><span>可以把模板文件存放在<span><code>templates</code><span>文件夹下面的不同目录中,然后使用<span><code>template.ParseGlob("templates/**/*.tmpl")</code><span>解析模板。</span></span></span></span></span></span></p>
</li>
</ol>
<h4 class="md-end-block md-heading"><span>14. 修改默认的标识符</span></h4>
<p><span class="md-line md-end-block"><span>Go标准库的模板引擎使用的花括号<span><code>{{</code><span>和<span><code>}}</code><span>作为标识,而许多前端框架(如<span><code>Vue</code><span>和 <span><code>AngularJS</code><span>)也使用<span><code>{{</code><span>和<span><code>}}</code><span>作为标识符,所以当我们同时使用Go语言模板引擎和以上前端框架时就会出现冲突,这个时候我们需要修改标识符,修改前端的或者修改Go语言的。这里演示如何修改Go语言模板引擎默认的标识符:</span></span></span></span></span></span></span></span></span></span></span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">template.New("index3.tmpl").Delims("{[","]}").ParseFiles("templates/index3.tmpl")</pre>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">&lt;h1&gt;hello {[ . ]}&lt;/h1&gt;</pre>
</div>
<h3 class="md-end-block md-heading"><span>五、text/template与html/tempalte的区别</span></h3>
<p><span class="md-line md-end-block"><span><code>html/template</code><span>针对的是需要返回HTML内容的场景,在模板渲染过程中会对一些有风险的内容进行转义,以此来防范跨站脚本攻击。</span></span></span></p>
<p><span class="md-line md-end-block"><span>例如,我定义下面的模板文件:</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:html;gutter:true;">&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="ie=edge"&gt;
    &lt;title&gt;Hello&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    {{.}}
&lt;/body&gt;
&lt;/html&gt;</pre>
</div>
<p><span class="md-line md-end-block"><span>这个时候传入一段JS代码并使用<span><code>html/template</code><span>去渲染该文件,会在页面上显示出转义后的JS内容。<span><code>&lt;script&gt;alert('嘿嘿嘿')&lt;/script&gt;</code><span> 这就是<span><code>html/template</code><span>为我们做的事。</span></span></span></span></span></span></span></span></p>
<p><span class="md-line md-end-block"><span>但是在某些场景下,我们如果相信用户输入的内容,不想转义的话,可以自行编写一个safe函数,手动返回一个<span><code>template.HTML</code><span>类型的内容。示例如下:</span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">func xss(w http.ResponseWriter, r *http.Request){
    tmpl,err := template.New("xss.tmpl").Funcs(template.FuncMap{
      "safe": func(s string)template.HTML {
            return template.HTML(s)
      },
    }).ParseFiles("./xss.tmpl")
    if err != nil {
      fmt.Println("create template failed, err:", err)
      return
    }
    jsStr := `&lt;script&gt;alert('嘿嘿嘿')&lt;/script&gt;`
    err = tmpl.Execute(w, jsStr)
    if err != nil {
      fmt.Println(err)
    }
}</pre>
</div>
<p><span class="md-line md-end-block"><span>这样我们只需要在模板文件不需要转义的内容后面使用我们定义好的safe函数就可以了。</span></span></p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="border-bottom: #e0e0e0 1px dashed; border-left: #e0e0e0 1px dashed; padding: 10px; font-family: 微软雅黑; font-size: 11px; border-top: #e0e0e0 1px dashed; border-right: #e0e0e0 1px dashed;overflow: hidden;" id="PSignature">
<div style="float: left; width: 10%">
   <img src="https://www.cnblogs.com/images/cnblogs_com/zhangyafei/1378866/o_o_Warning.png" style="width: 70px; height: 70px">
</div>
<div style="float: left; width: 90%; padding-top: 10px">
作者:张亚飞 <br>
出处:https://www.cnblogs.com/zhangyafei <br>
gitee:https://gitee.com/zhangyafeii <br>
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。<br>
</div>
</div><br><br>
来源:https://www.cnblogs.com/zhangyafei/p/12482244.html
頁: [1]
查看完整版本: Go标准库之html/template