html模板生成:

  • html/template包实现了数据驱动的模板,用于生成可对抗代码注入的安全HTML输出。它提供了和text/template包相同的接口,Go语言中输出HTML的场景都应使用text/template包。

模板语法

{{.}}

  • 模板语法都包含在{{和}}中间,其中{{.}}中的点表示当前对象。

  • 当我们传入一个结构体对象时,我们可以根据.来访问结构体的对应字段。例如:

// main.go
func sayHello(w http.ResponseWriter, r *http.Request) {
// 解析指定文件生成模板对象
tmpl, err := template.ParseFiles("./hello.html")
if err != nil {
fmt.Println("create template failed, err:", err)
return
}
var user = struct {
name string
age int
}{
name:"zhaohaiyu",
age:18,
}
// 利用给定数据渲染模板,并将结果写入w
tmpl.Execute(w, user)
}
func main() {
http.HandleFunc("/", sayHello)
err := http.ListenAndServe(":9090", nil)
if err != nil {
fmt.Println("HTTP server failed,err:", err)
return
}
}
Hello
姓名 {{.Name}}
年龄:{{.Age}}
同理,当我们传入的变量是map时,也可以在模板文件中通过.根据key来取值。

注释

{{/* a comment */}}

pipeline

  • pipeline是指产生数据的操作。比如{{.}}、{{.Name}}等。Go的模板语法中支持使用管道符号|链接多个命令,用法和unix下的管道类似:|前面的命令会将运算结果(或返回值)传递给后一个命令的最后一个位置。

注意:并不是只有使用了|才是pipeline。Go的模板语法中,pipeline的概念是传递数据,只要能产生数据的,都是pipeline。

变量

  • 我们还可以在模板中声明变量,用来保存传入模板的数据或其他语句生成的结果。具体语法如下:
$obj := {{.}}

其中$obj是变量的名字,在后续的代码中就可以使用该变量了。

移除空格

  • 有时候我们在使用模板语法的时候会不可避免的引入一下空格或者换行符,这样模板最终渲染出来的内容可能就和我们想的不一样,这个时候可以使用{{-语法去除模板内容左侧的所有空白符号, 使用-}}去除模板内容右侧的所有空白符号。

例如:{{- .Name -}}

注意:-要紧挨{{和}},同时与模板值之间需要使用空格分隔。

条件判断

  • Go模板语法中的条件判断有以下几种:
{{if pipeline}} T1 {{end}}
{{if pipeline}} T1 {{else}} T0 {{end}}
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}

range

Go的模板语法中使用range关键字进行遍历,有以下两种写法,其中pipeline的值必须是数组、切片、字典或者通道。

{{range pipeline}} T1 {{end}}

{{range pipeline}} T1 {{else}} T0 {{end}}

with

{{with pipeline}} T1 {{end}}

{{with pipeline}} T1 {{else}} T0 {{end}}

预定义函数

执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一般不在模板内定义函数,而是使用Funcs方法添加函数到模板里。

预定义的全局函数如下:

  1. and

    • 函数返回它的第一个empty参数或者最后一个参数;
    • 就是说"and x y"等价于"if x then y else x";所有参数都会执行;
  2. or
    • 返回第一个非empty参数或者最后一个参数;
    • 亦即"or x y"等价于"if x then x else y";所有参数都会执行;
  3. not
    • 返回它的单个参数的布尔值的否定
  4. len
    • 返回它的参数的整数类型长度
  5. index
    • 执行结果为第一个参数以剩下的参数为索引/键指向的值;
    • 如"index x 1 2 3"返回x[1][2][3]的值;每个被索引的主体必须是数组、切片或者字典。
  6. print
    • 即fmt.Sprint
  7. printf
    • 即fmt.Sprintf
  8. println
    • 即fmt.Sprintln
  9. html
    • 返回与其参数的文本表示形式等效的转义HTML。
    • 这个函数在html/template中不可用。
  10. urlquery
  • 以适合嵌入到网址查询中的形式返回其参数的文本表示的转义值。
  • 这个函数在html/template中不可用。
  1. js
  • 返回与其参数的文本表示形式等效的转义JavaScript。
  1. call
  • 执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;

    如"call .X.Y 1 2"等价于go语言里的dot.X.Y(1, 2);

    其中Y是函数类型的字段或者字典的值,或者其他类似情况;

    call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);

    该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型;

    如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;

比较函数

  • 布尔函数会将任何类型的零值视为假,其余视为真。

下面是定义为函数的二元比较运算的集合:

eq 如果arg1 == arg2则返回真

ne 如果arg1 != arg2则返回真

lt 如果arg1 < arg2则返回真

le 如果arg1 <= arg2则返回真

gt 如果arg1 > arg2则返回真

ge 如果arg1 >= arg2则返回真

为了简化多参数相等检测,eq(只有eq)可以接受2个或更多个参数,它会将第一个参数和其余参数依次比较,返回下式的结果:

{{eq arg1 arg2 arg3}}

比较函数只适用于基本类型(或重定义的基本类型,如”type Celsius float32”)。但是,整数和浮点数不能互相比较。

自定义函数

Go的模板支持自定义函数。

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)
}

我们可以在模板文件hello.tmpl中按照如下方式使用我们自定义的kua函数了。

{{kua .Name}}

嵌套template

我们可以在template中嵌套其他的template。这个template可以是单独的文件,也可以是通过define定义的template。

举个例子:t.tmpl文件内容如下:tmpl test

测试嵌套template语法 {{template "ul.tmpl"}}

{{template "ol.tmpl"}}

{{ define "ol.tmpl"}}
吃饭
睡觉
打豆豆
{{end}}

ul.tmpl文件内容如下:

注释

日志

测试

我们注册一个templDemo路由处理函数.

http.HandleFunc("/tmpl", tmplDemo)

tmplDemo函数的具体内容如下:

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)
}

注意:在解析模板时,被嵌套的模板一定要在后面解析,例如上面的示例中t.tmpl模板中嵌套了ul.tmpl,所以ul.tmpl要在t.tmpl后进行解析。

block

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

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

定义一个根模板templates/base.tmpl,内容如下:Go Templates

{{block "content" . }}{{end}}

然后定义一个templates/index.tmpl,”继承”base.tmpl:

{{template "base.tmpl"}}
{{define "content"}}
Hello world!
{{end}}

然后使用template.ParseGlob按照正则匹配规则解析模板文件,然后通过ExecuteTemplate渲染指定的模板:

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
}
}

如果我们的模板名称冲突了,例如不同业务线下都定义了一个index.tmpl模板,我们可以通过下面两种方法来解决。

  1. 在模板文件开头使用{{define 模板名}}语句显式的为模板命名。
  2. 可以把模板文件存放在templates文件夹下面的不同目录中,然后使用template.ParseGlob("templates/**/*.tmpl")解析模板。

修改默认的标识符

Go标准库的模板引擎使用的花括号{{和}}作为标识,而许多前端框架(如Vue和AngularJS)也使用{{和}}作为标识符,所以当我们同时使用Go语言模板引擎和以上前端框架时就会出现冲突,这个时候我们需要修改标识符,修改前端的或者修改Go语言的。这里演示如何修改Go语言模板引擎默认的标识符:

template.New("test").Delims("{[", "]}").ParseFiles("./t.tmpl")

text/template与html/tempalte的区别

html/template针对的是需要返回HTML内容的场景,在模板渲染过程中会对一些有风险的内容进行转义,以此来防范跨站脚本攻击。

例如,我定义下面的模板文件:

Hello
{{.}}

这个时候传入一段JS代码并使用html/template去渲染该文件,会在页面上显示出转义后的JS内容。alert('嘿嘿嘿')这就是html/template为我们做的事。

但是在某些场景下,我们如果相信用户输入的内容,不想转义的话,可以自行编写一个safe函数,手动返回一个template.HTML类型的内容。示例如下:

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 := `alert('嘿嘿嘿')`
err = tmpl.Execute(w, jsStr)
if err != nil {
fmt.Println(err)
}
}

这样我们只需要在模板文件不需要转义的内容后面使用我们定义好的safe函数就可以了。{{ . | safe }}

代码生成

生成代码用text/template包,模板语法和html/template一样

package main
import (
"fmt"
"html/template"
"os"
)
var data = `
package main
import "fmt"
func main() {
str := "{{ . }}"
for _, v := range str {
fmt.Println(v)
}
}
`
func main() {
t := template.New("main")
t, err := t.Parse(data)
if err != nil {
fmt.Println("解析失败")
return
}
var str = "zhaohaiyu"
file, err := os.OpenFile("./print/main.go", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
fmt.Printf("open file:%s failed, err:%v\n", "./print/main.go", err)
return
}
err = t.Execute(file, str)
if err != nil {
fmt.Println("execute failed err:",err)
return
}
return
}

生成的代码:

package main
import "fmt"
func main() {
str := "zhaohaiyu"
for _, v := range str {
fmt.Println(v)
}
}

参考文章:

golang 模板 html/template与text/template的更多相关文章

  1. [golang]text/template模板

    这个可以用来处理text文本,不过我更偏爱做成代码生成器. [golang]text/template模板 package main import ( "os" "tex ...

  2. golang "text/template" 模板语法简明教程

    转自:https://www.cnblogs.com/Pynix/p/4154630.html [模板标签] 模板标签用"{{"和"}}"括起来   [注释] ...

  3. go语言的模板,text/template包

    go语言的模板,text/template包 定义 模板就是将一组文本嵌入另一组文本里 传入string--最简单的替换 package main import ( "os" &q ...

  4. 前端模板<script type="text/template" id="tmpl">

    前端模板, 比连接字符串好用多了, 还可以使用循环\判断等语句, 减少工作量 <script type="text/template" id="member-tmp ...

  5. 关于MVC模板渲染的一点小事type="text/template"

    先上一个demo,简单粗暴,请自便 <!DOCTYPE html> <html> <head lang="en"> <meta chars ...

  6. script "text/template"

    <script type="text/template" id="orgItem"> <div class="{orgClass}& ...

  7. Text Template Transformation Toolkit

    Text Template Transformation Toolkit       1.且算简介         笔者以一个英文字母和一个数字取了一个简单的名字.名唤"T4"(名 ...

  8. go标准库的学习-text/template

    参考:https://studygolang.com/pkgdoc 导入方式: import "text/template" template包实现了数据驱动的用于生成文本输出的模 ...

  9. <script type="text/template">是干什么的,为什么要把html写在js中? 这是什么编程语言风格,都能这样用吗?

    这一段存放了一个模板.在js里面,经常需要使用js往页面中插入html内容.比如这样: var number = 123; $('#d').append('<div class="t& ...

随机推荐

  1. 第九篇 -- cpu的学习

    CPU的核数关系: 总核数 = 物理CPU个数 X 每颗物理CPU的核数 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 Linux中查看CPU数: # 查看物理CPU个数 ...

  2. 01MATLAB导论

    MATLAB语言的主要功能 数值计算 符号计算 图形绘制 程序流程控制 工具箱 课程的学习目标 要求理解MATLAB功能实现的数学背景与算法原理 掌握利用MATLAB进行问题求解的基本规律 能够利用M ...

  3. jvm源码解读--19 Java的join()方法解读 以及 invokestatic 字节码 执行 流程图

  4. intouch 开发源程序加密方法

    在先前项目中,因为同行竞争被拷贝走了源程序代码,以至于被上司责备,故而亡羊补牢对intouch(10.1老版本进行源代码加密探索)整理方法如下. 1.intouch wondermarker打开源程序 ...

  5. 🔥 LeetCode 热题 HOT 100(21-30)

    46. 全排列 思路:典型回溯法 class Solution { public List<List<Integer>> permute(int[] nums) { Linke ...

  6. 使用 Python 翻译 CHM 帮助文档

    此方法绝对切实可行 翻译成品项目: https://github.com/foyoux/InstallShield2020-Documents 效果预览 安装 docts 库 pip install ...

  7. (原创)[.Net] 进程间通信框架(基于共享内存)——SimpleMMF

    一.前言 进程间通信技术的应用非常广泛,在Windows下常用的实现方式有:管道.Socket.消息.本地文件.共享内存等,每种方式都有各自适应的场景. 在进行大数据交换时,最优的方式便是共享内存. ...

  8. 四、C#简单操作MinIO

    MinIO的官方网站非常详细,以下只是本人学习过程的整理 一.MinIO的基本概念 二.Windows安装与简单使用MinIO 三.Linux部署MinIO分布式集群 四.C#简单操作MinIO He ...

  9. LaTex公式语法教程及手册(附emlogpro公式显示插件katex说明)

    目录 第一列 第二列 第三列 效果 求和(使用\sum标签) 文本效果 本插件简介 积分(使用\int标签) 文本大小 LaTex是什么 空格 特殊符号 LaTex公式使用教程及手册 定界符 LaTe ...

  10. ubuntu 权限管理设置

    最近工作中涉及文件操作的内容较多,所以会出现各种各样的权限不足问题,导致操作失败.下面就来讲解下我碰到这种问题是通过什么的方法解决的 一.用户和权限 用户 是 Linux 系统工作中重要的一环,用户管 ...