本文介绍了在golang中使用tmpl模板,记录了一些使用方法及常用的模板语法,最后的最后就是遇到的一个坑。
在前后端不分离的开发场景中,我们需要将一些数据动态的渲染到文档中,实现动态效果。我们就可以通过模板渲染的方式进行。
这里的模板就是指事先定义好的一些文档文件,渲染就是通过类似文本替换的操作,使用相应的数据去替换文档中事先定义好的一些标记。
golang中内置了两种模板引擎,text/template 和 html/template (这里再实际应用中还碰到了一个坑,后边再说)
这里需要按照相关语法去编写。
func (t *Template) Parse(text string) (*Template, error)
func (t *Template) ParseFiles(filenames ...string) (*Template, error)
func (t *Template) ParseGlob(pattern string) (*Template, error)
func (t *Template) Execute(wr io.Writer, data any)
{{.FieldName}}
模板语法都包含在{{}}中间,其中的.表示当前对象。
当我们传入一个对象时,使用{{.FieldName(字段名)}}的方式来访问。
import (
"os"
"strings"
"text/template"
)
var tmplStr = `Hello {{.Name}}`
type Student struct {
Name string
}
func main() {
tmpl := template.Must(template.New("").Parse(tmplStr))
stu := Student{
Name: "张三",
}
err := tmpl.Execute(os.Stdout, stu)
if err != nil {
return
}
}
变量
可以在模板中声明变量
$obj := {{.FieldName}}
判断
模板中支持if语句,语法:{{if 判断条件}}{{else}}{{end}}
var tmplStr = `{{if .IsStu}}{{.Name}}是一个学生{{else}}{{.Name}}不是一个学生{{end}}`
type Student struct {
Name string
IsStu bool
}
func main() {
tmpl := template.Must(template.New("").Parse(tmplStr))
stu := Student{
Name: "张三",
IsStu: true,
}
err := tmpl.Execute(os.Stdout, stu)
if err != nil {
return
}
}
控制台输出:张三是一个学生
循环
模板中可以使用range 语句进行循环处理,语法:{{range .数据字段名}}{{end}}
var tmplStr = `{{range .Grade}}学科:{{.Name}}的成绩是 {{.Score}}{{end}}`
type Student struct {
Name string
IsStu bool
Grade []Subject
}
type Subject struct {
Name string
Score int
}
func main() {
tmpl := template.Must(template.New("").Parse(tmplStr))
stu := Student{
Name: "张三",
IsStu: true,
Grade: []Subject{
{Name: "语文", Score: 90},
{Name: "数学", Score: 80},
{Name: "英语", Score: 70},
},
}
err := tmpl.Execute(os.Stdout, stu)
if err != nil {
return
}
}
控制台输出:学科:语文的成绩是 90学科:数学的成绩是 80学科:英语的成绩是 70
在循环中直接使用数组中对象的字段就可以了
比较
eq 如果arg1 == arg2则返回真
ne 如果arg1 != arg2则返回真
lt 如果arg1 < arg2则返回真
le 如果arg1 <= arg2则返回真
gt 如果arg1 > arg2则返回真
ge 如果arg1 >= arg2则返回真
eq配合if进行使用
var tmplStr = `{{if eq .Name "张三"}}班长{{else}}普通学生{{end}}`
type Student struct {
Name string
IsStu bool
Grade []Subject
}
type Subject struct {
Name string
Score int
}
func main() {
tmpl := template.Must(template.New("").Parse(tmplStr))
stu := Student{
Name: "张三",
IsStu: true,
Grade: []Subject{
{Name: "语文", Score: 90},
{Name: "数学", Score: 80},
{Name: "英语", Score: 70},
},
}
err := tmpl.Execute(os.Stdout, stu)
if err != nil {
return
}
}
控制台输出:班长
自定义函数
在实际使用中,模板引擎中提供的函数不能满足需求,这个时候就可以自定义一些函数注册到模板引擎中进行使用,示例如下:
var tmplStr = `{{if Contains .Hobby "学习"}}{{.Name}}喜欢学习{{else}}他不喜欢{{end}}`
type Student struct {
Name string
IsStu bool
Grade []Subject
Hobby string
}
type Subject struct {
Name string
Score int
}
func main() {
parse, err := template.New("").Funcs(template.FuncMap{
"Contains": Contains,
}).Parse(tmplStr)
if err != nil {
return
}
stu := Student{
Name: "张三",
IsStu: true,
Hobby: "游泳,学习,看书,游戏",
}
err = parse.Execute(os.Stdout, stu)
if err != nil {
return
}
}
// Contains 判断字符串是否包含
func Contains(s, t string) bool {
return strings.Contains(s, t)
}
控制台输出:张三喜欢学习
这里定义了一个 Contains 函数 用于判断字符串中是否包含子串,使用 Funcs 函数进行自定义函数的注册。
注:自定义函数的首字母一定要大写,不然模板引擎中访问不到该函数。
text/template
是将内容都已text文本格式返回。
html/tempalte
针对的是需要返回HTML内容的场景。 在模板渲染过程中会对一些有风险的内容进行转义,以此来防范跨站脚本攻击。
当传入一段JS代码并使用html/template
去渲染该文件,会在页面上显示出转义后的JS内容。 <script>alert('这是一个弹框')</script>
这就是html/template
为我们做的事。 但当有人恶意传入一个死循环的JS内容或一些占用资源比较大的内容,将会造成危险。
在编写代码生成器时,使用了模板语法,一开始并未注意使用的是html/tempalte。
在模板中进行变量输出时,老是会在变量两边加上 双引号“” 网上的解释是 ”如果 .ObjectName
是一个字符串类型的变量,生成的代码会默认添加双引号“
从网上找到两种解决方式,两种方法都是对字符串进行处理。
1:使用 printf
函数:在模板中,您可以使用 printf
函数来生成不带双引号的代码。
name: '{{printf "%s" .ObjectName}}Add',
// 把值传回了{{printf "%s" .ObjectName}}Add
props: {
visible: Boolean, // 更新了<{{printf "%s" .ObjectName}}Add v-model:visible
name: String
},
2:使用 strings.Trim
函数来删除生成的代码中的双引号
strings.Trim(s, "\"")
发现都不好用,还是不起作用。
不用猜了,使用 text/template
包就好了,具体原因还不清楚,有空了还需要研究一下。
依据template做了一个代码生成器,目前能根据数据库的pdm文件或连接MySQL数据库生成基本的增删改查功能,包含golang+vue+js的代码,有兴趣的可以看一下。 项目地址:https://github.com/majingzhen/codeGenerator-Go