小言_互联网的博客

GoLang—模版引擎text/template和html/template(下)

448人阅读  评论(0)

变量

我们知道,路由的处理函数可以向模版传递数据(该数据我们称为参数),传递的数据在模版中以一个点(.)表示。除此之外,模版里还可以定义变量,变量以美元符号($)开头,就像这样:

{{ range $key, $value := . }}
 <p>The key is {{ $key }} and the value is {{ $value }}</p>
{{ end }}

管道

管道类似Django的模版过滤器,简单来说就是将参数或变量进行格式转换或特殊处理等操作,使用方法如下。

<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Go Web Programming</title>
 </head>
 <body>
  {{ 12.3456 | printf "%.2f" }}
 </body>
</html>

上述代码通过管道将数字12.3456传递给了printf函数,并在printf函数的第一个参数中指定了格式指示符(specifier),最终,这个管道将返回12.35作为结果。

函数

管道是通过使用函数来处理参数或变量的数据内容,但有时候,内置的函数无法满足我们的开发需求,因此可以自定义模板函数,定义过程如下

package main

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

func formatDate(t time.Time) string {
  layout := "2006-01-02"
  return t.Format(layout)
}

func process(w http.ResponseWriter, r *http.Request) {
       # 将函数formatDate绑定到 template的FuncMap结构,并且命名为fdate
  funcMap := template.FuncMap { "fdate": formatDate }
  # 创建新模版tmpl.html,并将funcMap注册到模版tmpl.html
  t := template.New("tmpl.html").Funcs(funcMap)
  # 语法分析模版tmpl.html
  t, _ = t.ParseFiles("tmpl.html")
  # 调用模版tmpl.html
  t.Execute(w, time.Now())
}

func main() {
  server := http.Server{
    Addr: "127.0.0.1:8080",
  }
  http.HandleFunc("/process", process)
  server.ListenAndServe()
}

下一步在tmpl.html模板中使用自定义函数formatDate,如下所示。

<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Go Web Programming</title>
 </head>
 <body>
 	# 通过管道的形式使用自定义函数formatDate
  <div>The date/time is {{ . | fdate }}</div>
 </body>
</html>

除此之外,我们也可以像调用普通函数一样,将点(.)作为参数传递给fdate函数,具体做法如下

<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Go Web Programming</title>
 </head>
 <body>
  <div>The date/time is {{ fdate . }}</div>
 </body>
</html>

以上两种调用方式会产生相同的结果,但使用管道比直接调用函数要强大和灵活得多。如果用户定义了多个函数,那么他就可以通过管道将一个函数的输出传递给另一个函数作为输入,从而以不同的方式组合使用这些函数;尽管普通的函数调用也能够做到这一点,但使用管道可以产生更简单且更可读的代码。

上下文感知

上下文感知特性主要用于实现自动的防御编程,并且它使用起来非常方便。通过根据上下文对内容进行修改,Go 模板可以防止某些明显并且低级的编程错误。简单来说,上下文感知就是将路由的处理函数传递的数据(参数)是否进行转义处理。如果参数内容设有HTML语法,模版引擎会将HTML语法当成字符串输出,这样能够防止基于JavaScript、CSS 甚至 URL 的 XSS 攻击,默认情况下,模版引擎是开启HTML转义机制
如果想要允许用户输入 HTML 代码或者 JavaScript 代码,并在显示内容时执行这些代码,可以使用 Go 提供的“不转义 HTML”机制:只要把不想被转义的内容传给template.HTML函数,模板引擎就不会对其进行转义,实现方法如下。

func process(w http.ResponseWriter, r *http.Request) {
    # 设置请求头,关闭X-XSS-Protection
  w.Header().Set("X-XSS-Protection", "0")
  t, _ := template.ParseFiles("tmpl.html")
  # 调用模版tmpl.html时,使用template.HTML(r.FormValue("comment"))关闭转义
  t.Execute(w, template.HTML(r.FormValue("comment")))
}

嵌套模板

模版的嵌套在上一节已讲述包含动作,就是在一个模版文件里引用另外一个模版文件,此外,还可以在一个模板文件里定义多个模版,如下所示。

# 模版文件layout.html
# 定义模版layout
{{ define "layout" }}
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>Go Web Programming</title>
 </head>
 <body>
 	# 调用模版content
  {{ template "content" }}
 </body>
</html>
{{ end }}

# 定义模版content
{{ define "content" }}
Hello World!
{{ end }}

我们还可以在另外的模版文件定义模版content,首先移除layout.html文件中现有的content模板定义,然后在模版文件red_hello.html和blue_hello.html定义模版content,代码如下。

# 模版文件red_hello.html
{{ define "content" }}
<h1 style="color: red;">Hello World!</h1>
{{ end }}
# 模版文件blue_hello.html
{{ define "content" }}
<h1 style="color: blue;">Hello World!</h1>
{{ end }}

由于模版content在模版文件red_hello.html和blue_hello.html均有定义,因此在路由的处理函数根据不同情况执行相应的调用方式,如下所示。

func process(w http.ResponseWriter, r *http.Request) {
  rand.Seed(time.Now().Unix())
  var t *template.Template
  if rand.Intn(10) > 5 {
    t, _ = template.ParseFiles("layout.html", "red_hello.html")
  } else {
    t, _ = template.ParseFiles("layout.html", "blue_hello.html")
  }
  t.ExecuteTemplate(w, "layout", "")
}

上述代码随机生成一个整数,根据数值,模版文件layout.html调用相应的模版文件(red_hello.html和blue_hello.html),从模版文件(red_hello.html和blue_hello.html)选择相应的模版content。

块动作定义默认模板

Go 1.6引入了一个新的块动作(block action),这个动作允许用户定义一个模板并且立即使用。我们将模版嵌套的代码进行修改,代码如下

func process(w http.ResponseWriter, r *http.Request) {
  rand.Seed(time.Now().Unix())
  var t *template.Template
  if rand.Intn(10) > 5 {
    t, _ = template.ParseFiles("layout.html", "red_hello.html")
  } else {
    t, _ = template.ParseFiles("layout.html")
  }
  t.ExecuteTemplate(w, "layout", "")
}

当程序执行else的代码时,模版文件layout.html是使用{{ template “content” }}调用模版content,但代码中并没有调用模版文件blue_hello.html的模版content,因此程序出现异常信息,为了解决这一文件,我们可以在模版文件layout.html添加默认模板content,代码如下。

{{ define "layout" }}
< html>
 < head>
  < meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  < title>Go Web Programming< /title>
 < /head>
 < body>
  {{ block "content" . }}
   < h1 style="color: blue;">Hello World!< /h1>
  {{ end }}
 < /body>
< /html>
{{ end }}

块动作能够高效地定义一个content模板,并将它放置到layout模板里面。当layout模板被执行时,如果模板引擎没有找到可用的content模板,那么它就会使用块动作中定义的content模板。


转载:https://blog.csdn.net/HuangZhang_123/article/details/101525868
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场