Go html/template

JunChow520 · · 649 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

  • Golang的html/template包实现了数据驱动的模板,用于生成可对抗代码注入且安全HTML输出。
  • html/template包提供了和text/template包相同的接口,html/template是对text/template的二次封装并增加了安全性的处理。

模板渲染存在跨站脚本攻击的风险,本质上是网站将用户的输入不作转义写入到生成的页面中。若用户提交一段浏览器脚本,则会在用户页面中执行,进而产生不可预知的风险。

html/template会自动开启安全模式将需要编码的数据处理成纯文本,各种不同的转义上下文可以安全的嵌入HTML模板,比如JavaScript、CSS、URI上下文。

作用机制

  • 模板文件通常定义为.tmpl.tpl为后缀,也可使用其他后缀。
  • 模板文件必须使用UTF8编码
  • 模板文件使用{{}}作为分界符包裹和标识需要传入的数据
  • 传递给模板的数据通过点号.来访问,若数据是结构体或哈希则通过{{.FieldName}}来访问字段。
  • {{}}包裹内容外其他内容军不做修改原样输出

分界符

  • 模板文件使用{{}}作为分界符包裹和标识需要传入的数据
  • 可使用template实例的Delims()方法修改默认分界符
//正则匹配解析模板
tpl, err := template.ParseGlob("./view/*.html")
if err != nil {
    fmt.Printf("create template failed, %v\n", err)
}
//修改定界符
tpl.Delims("{[", "]}")

使用流程

  1. 定义模板文件

定义模板文件时需按照相关语法规则去编写

  1. 解析模板文件

使用模板解析方法获取模板对象

func (t *Template) Parse(src string) (*Template, error)
func ParseFiles(filenames ...string) (*Template, error)
func ParseGlob(pattern string) (*Template, error)

也可使用New()方法创建名为name的模板实例

func New(name string) *Template

对模板实例调用上面的方法去解析模板字符串或模板文件

  1. 模板渲染

使用数据填充模板

func (t *Template) Execute(wr io.Writer, data interface{}) error
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error

例如:

创建入口文件

$ vim main.go
package main

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

func main() {
    http.HandleFunc("/", defaultHandler)
    err := http.ListenAndServe(":9090", nil)
    if err != nil {
        fmt.Printf("http server start failed, %v\n", err)
    }
}

func defaultHandler(rw http.ResponseWriter, rq *http.Request) {
    //定义模板
    tplname := "./view/default.html"
    //解析模板
    tpl, err := template.ParseFiles(tplname)
    if err != nil {
        fmt.Printf("parse template failed, %v\n", err)
        return
    }
    //渲染模板
    data := "admin"
    err = tpl.Execute(rw, data)
    if err != nil {
        fmt.Printf("render template faild, %v\n", err)
        return
    }
}

创建模板文件

$ vim ./view/default.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    {{.}}
</body>
</html>

测试访问

$ curl -i http://127.0.0.1:9090

模板语法

  • {{.}}中的点表示当前对象

渲染map类型

//渲染模板
data := map[string]interface{}{"Id": 1, "Name": "admin"}
err = tpl.Execute(rw, data)
<body>
    {{.}}
    <p>{{.Id}}</p>
    <p>{{.Name}}</p>
</body>

渲染struct类型

type User struct {
    Id   int
    Name string
}
//渲染模板
data := User{Id: 100, Name: "admin"}
err = tpl.Execute(rw, data)
<body>
    {{.}}
    <p>{{.Id}}</p>
    <p>{{.Name}}</p>
</body>

渲染列表

//渲染模板
list := []string{"manager", "administrator", "operator"}
data := map[string]interface{}{"id": 1, "list": list}
err = tpl.Execute(rw, data)
{{range $index,$item := .list}}
<p>{{$index}} - {{$item}}</p>
{{end}}

模板嵌套

define

{{define "tplname"}}

模板文件中建议为每个模板文件显示地定义模板名称,否则会因模板对象名与模板名不一致,无法解析。

在模板中嵌入其他模板,嵌入的模板可以是单独的文件,也可以是通过define定义的模板。

$ vim ./view/header.html
{{define "header"}}
<div>header {{$.id}}</div>
{{end}}

template

  • 使用{{template "tplName" .}}引入其他模板
  • 使用.点号访问当前数据域
  • 使用$.访问绝对顶层数据域
//定义模板
layout := "./view/layout.html"
tplname := "./view/default.html"
//解析模板
tpl, err := template.ParseFiles(layout, tplname)

解析模板时被嵌套的模板一定要在后面解析

$ vim ./view/layout.html
{{define "layout"}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>LAYOUT {{$.id}}</h1>
    {{template "header" .}}
    <div>{{block "content" .}}{{end}}</div>
</body>
</html>
{{end}}

被嵌套模板无法渲染变量

$ vim ./view/default.html
{{template "layout" .}}

{{define "content"}}
<p>default</p>
{{end}}

block

{{block "name" pipeline}}
T1
{{end}}

block是定义模板{{define "name"}}T1{{end}}和执行{{template "name" pipeline}}的缩写,典型用法是定义一组模板,通过在其中重新定义块模块进行自定义。

定义基础布局父模板

$ vim view/layout.html
{{define "layout"}}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>LAYOUT {{$.id}}</h1>
    {{template "header" .}}
    <div>{{block "content" .}}{{end}}</div>
</body>
</html>
{{end}}

定义子模板继承自父模板

$ vim ./view/default.html
{{template "layout" .}}

{{define "content"}}
<p>default</p>
{{end}}

使用template.ParseGlob按正则匹配规则解析模板,再通过ExecuteTemplate渲染指定的模板。

func defaultHandler(rw http.ResponseWriter, rq *http.Request) {
    //正则匹配解析模板
    tpl, err := template.ParseGlob("./view/*.html")
    if err != nil {
        fmt.Printf("create template failed, %v\n", err)
    }
    //渲染模板
    name := "default.html"
    list := []string{"manager", "administrator", "operator"}
    data := map[string]interface{}{"id": 1, "list": list}
    err = tpl.ExecuteTemplate(rw, name, data)
    if err != nil {
        fmt.Printf("render template faild, %v\n", err)
        return
    }
}

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:JunChow520

查看原文:Go html/template

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

649 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传