Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Go语言中的模板引擎

7
May
2018

Go语言中的模板引擎

By Alex
/ in Go
4 Comments
template包

包text/template实现了一个数据驱动的模板引擎,类似的还有html/template包,两者接口一样,但是后者针对HTML进行处理,可以防止某些注入式攻击。

模板的源码是一段UTF-8文本,其中会有一些 {{ }}包围的动作(Action)。模板执行时,动作中的内容 —— pipeline —— 被计算、替换,外部的内容则直接原样输出。一旦模板被解析,就可以被多次、并行的执行。

{{}}内部可以包含以点号开头的表达式,这表示读取上下文对象(dot对象)的属性。例如 {{.name}}则意味着读取上下文对象的name属性。

{{-可以用于去除左外侧的空白符(空格、水平制表、回车、换行), -}}则可以清除右外侧的空白符,例如:

Go
1
2
3
"{{1 -}} < {{- 2}}"
// 输出
1<2
动作
{{/* a comment */}} 注释,执行模板时直接被丢弃,支持多行注释
{{pipeline}},流水线(一系列Go表达式或者Go表达式通过管道操作符连接)的文本展示形式(即fmt.Print输出形式),直接输出到结果
Go
1
2
3
{{if pipeline}} T1 {{end}}   // 如果pipeline计算结果非空(false、0、nil)则输出T1
{{if pipeline}} T1 {{else}} T0 {{end}}
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}

 条件分支结构,T*可以是普通文本,或者其它任何Action

Go
1
2
3
4
// pipeline的计算结果必须是数组、切片、映射或者通道。如果值长度为0则不输出任何东西
{{range pipeline}} T1 {{end}}
// 如果pipeline为空,则上下文对象不变且执行T0
{{range pipeline}} T1 {{else}} T0 {{end}}

 循环结构,T1中的{{}}内的点号(dot对象,即上下文对象)被设置为数组、切片、映射或通道的元素

range可以声明变量: range $index, $element := pipeline

{{template "name"}} 就地引入并执行名为name的模板

{{template "name" pipeline}} 就地引入并执行名为name的模板,设置该模板的dot对象为pipeline的结果

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

相当于定义模板 {{define "name"}} T1 {{end}}并原地执行 {{template "name" .}}

{{with pipeline}} T1 {{end}}如果pipeline结果不为空则设置dot对象为pipeline的结果,并执行T1 

{{with pipeline}} T1 {{else}} T0 {{end}}如果pipeline结果不为空则设置dot对象为pipeline的结果,并执行T1;否则dot对象不变且执行T0

流水线

所谓流水线,类似于Linux的命令管道,是一系列“命令”的链条。每个命令可以是:

  1. 简单的值: Argument
  2. 方法调用: .Method [Argument...]
  3. 函数调用: functionName [Argument...]

这些命令通过 | 符号连接在一起,前一个命令的结果作为后一个命令的最后一个入参传递。流水线中最后一个命令的结果,作为整个流水线的结果。命令的结果(返回值)可以1-2个,如果第2个值非nil则意味着出现错误,模板调用者会收到此错误。

简单值

也叫Argument,可以是如下形式之一:

  1. 各种类型的字面值,以及nil
  2. 点号( .),上下文对象(结构体,dot)
  3. 键值访问,针对map对象,例如 .key、 $map.key,可以和字段访问混合进行点号导航,键值访问不像字段访问,key不需要首字母大写
  4. 字段访问,例如 .Field、.Field1.Field2、 $v.Field1.Field2
  5. .Method,上下文对象为接收者的方法调用
  6. .func,函数调用
  7. $varname,模板变量,美元符号开头
变量

任何一个Action中的Pipeline都可以初始化变量,示例: {{ $variable := pipeline }}

变量总是以$符号开头,不管是声明还是后续引用。声明过的变量可以被赋值:

Go
1
$variable = pipeline

 range动作可以声明变量,并且在循环体内部使用:

Go
1
range $index, $element := pipeline 
根对象

当作用域改变后,你可以使用 $来访问解析模板最初使用的根对象,例如 $.Chart.Name

函数

在执行模板时,根据名称,首先寻找为模板绑定的函数:

Go
1
2
3
4
5
            // 创建模板                绑定函数
tmpl, err = template.New("hello").Funcs(template.FuncMap{
  "hasPermission": func(user User, feature string) bool {
  },
}).ParseFiles("hello.tpl") 

如果找不到则寻找全局函数。全局函数列表:

函数 说明
and 返回所有参数的逻辑与结果,实际上就是返回第一个空参数或者最后一个参数
call 调用第一个参数,后续参数作为入参。例如 call .X.Y 1 2等价于 .X.Y(1, 2)
html 返回参数的文本展示形式,并执行HTML转义
index 返回第一个参数指定索引上的值。例如 index x 1 2 3等价于 x[1][2][3]
js 返回参数的文本展示形式,并执行JavaScript转义
len 返回参数的长度
not 逻辑非操作
or 返回所有参数的逻辑或操作,实际上就是返回第一个非空参数或最后一个参数
print fmt.Sprint
printf fmt.Sprintf
println fmt.Sprintln
urlquery 返回参数的文本展示形式,并进行处理保证其适合作为HTTP查询串
作为函数的二元操作符
eq arg1 == arg2
ne arg1 != arg2
lt arg1 < arg2
le arg1 <= arg2
gt arg1 > arg2
ge arg1 >= arg2
关联模板

每个模板都具有名称,在创建模板时你需要给出此名称。此外,每个模板都可以关联0-N个它可以调用的其它模板,这些关联是传递性的。关联模板可以用define来声明:

Go
1
2
3
4
5
6
7
8
9
10
//             定义关联模板
tmplText := `{{define "T1"}}ONE{{end}}TWO`
//                      给出模板名称
tmpl, _ := template.New("test").Parse(tmplText)
 
// 执行模板
tmpl.Execute(os.Stdout, nil) // 输出TWO
 
// 执行指定的关联模板(Associated templates)
tmpl.ExecuteTemplate(os.Stdout, "T1", nil) // 输出ONE
编程接口
Go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type User struct {
    Name string
    Age  uint8
}
 
func main() {
    # 从文本解析出一个模板
    tmpl, _ := template.New("hello").Parse(`
{{- range $key, $val := . -}}
{{$key}} = {{ $val.Name }}, {{ $val.Age }}
{{ end -}}
`)
    users := make(map[string]User)
    users["10000"] = User{"Alex", 32}
    users["10001"] = User{"Meng", 29}
    // 执行模板,最后一个参数为上下文对象
    err := tmpl.Execute(os.Stdout, users)
    if err != nil {
        log.Fatalf(err.Error())
    }
}
sprig包

包github.com/Masterminds/sprig扩充了template包的函数库。要使用此函数库,调用template.Funcs:

Go
1
2
3
4
5
6
7
8
9
import (
    "html/template"
    "github.com/Masterminds/sprig"
)
 
func main() {
    tmplText := `{{}}`
    tmpl, _ := template.New("test").Funcs(sprig.FuncMap()).Parse(tmplText)
}
日期函数 
函数 说明
date date FORMAT TIME:格式化日期,FORMAT为格式化字符串,TIME为time.Time
dateModify date OFFSET TIME:修改给出的时间,例如 dateModify "-1.5h" now
now 返回当前时间
htmlDate 返回用于HTML中date类型表单字段的时间字符串
dateInZone dateInZone FORMAT TIME TZ:格式化为指定时区
htmlDateInZone htmlDateInZone TIME TZ:返回用于HTML中date类型表单字段的时间字符串,转换为指定时区
字符串函数
函数 说明
abbrev 缩写参数,超出的字符以...代替。例如 abbrev 5 "hello world"输出 he...
abbrevboth abbrevboth N STR:从双侧缩写
trunc trunc N STR:截断到指定长度
trim 去除空白
trimAll trimAll T STR:去除所有指定字符。例如 trimAll "$" "$5.00"输出 5.00
trimSuffix trimSuffix T STR:去除结尾的指定字符
trimPrefix trimPrefix T STR:去除前导的指定字符
upper 转换为大写
lower 转换为小写
nospace 移除所有空格
title string.Title
untitle 移除Title Case
repeat repeat N STR:重复指定次数
substr substr STR START LEN,截取子串:
Go
1
.Release.Namespace | substr 0 3 | eq "dev"
initials 返回字符串中每个单词的首字母组成的缩写字符串
randAlphaNum randAlphaNum N:返回指定长度的随机字符串
randAlpha
randAscii
randNumeric
swapcase 切换大小写
shuffle 随机改变字符串中字符的顺序
snakecase 驼峰式大写转换为下划线
camelcase 下划线后的小写字符转换为大写,去除下划线
wrap wrap N STR:在指定长度处插入换行
wrapWith wrapWith N W STR:在指定长度处插入字符串W
contains contains SUB STR:是否包含子串
hasPrefix hasPrefix SUB STR:是否包含指定前缀
hasSuffix hasSuffix SUB STR:是否包含指定后缀
quote 以双引号包围,将"转义为\"
squote 以双引号包围,不进行转义
cat CAT S1 S2... 连接字符串,用空格
indent indent N STR:缩进字符串,例如 indent 4 "foo\nbar"输出     foo\n    bar 
nindent indent N STR:缩进字符串,并且为每行前添加一个换行符
replace 替换出现的子串,例如 $name | replace " " "-"
sha256sum 生成sha256哈希
toString 转换为字符串
切片函数
函数 说明
字符串切片
join join SEP SLICE:使用SEP来连接一个切片
split split SEP STRING:使用SEP来分割字符串。返回一个键为_N的映射,其中N为子串索引(0开始)
splitList 类似上面,返回字符串数组
toStrings 返回一个列表的字符串形式,例如 list 1 2 3 | toStrings输出 ["1" "2" "3"]
sortAlpha 按照字典序排列一个列表
整数切片
until until N:返回从0-N组成的切片
untilStep untilStep START STOP STEP
类型转换
函数 说明
atoi 字符串转换为整数,无法解析则返回0
int64 将字符串或者其它数字类型转换为int64
int 将字符串或者其它数字类型转换为int
float64 将字符串或者其它数字类型转换为float64
默认值函数
函数 说明
default default DEFAULT VALUE:如果VALUE为空则返回DEFAULT
empty empty VALUE:如果VALUE是其类型的零值则返回true,注意:结构绝不会返回true,应该使用if判断其是否为空
coalesce coalesce V1 V2 ... 返回第一个非空值
compact compact V1 V2 ... 返回非零值组成的列表
编解码函数
函数 说明
b64enc 执行Base64编码
b64dec 执行Base64解码
genPrivateKey 生成指定算法的私钥,支持的算法rsa、dsa、ecdsa
derivePassword 根据Master Password算法衍生密码
系统函数
函数 说明
env 提取环境变量
expandenv 通过环境变量来展开字符串。例如 expandenv "$HOME"输出/home/alex
base 返回文件路径的最后一段
dir 移除文件路径的最后一段
ext 获取扩展名
isAbs 是否绝对路径
数学函数
函数 说明
add1 add1 N 返回 N + 1
add 任意数量整数求和
sub 减法操作
div 除法操作
mod 取模操作
mul 乘法操作
max 返回一组整数中的最大值
min 返回一组整数中的最小值
集合函数
函数 说明
tuple 将一系列条目转换为元组
list 将一系列条目转换为列表,优先于tuple
dict 将一系列键值对转换为 map[string]interface{}
list操作
first 返回列表第一个元素
last 返回列表最后个元素
rest 返回除了第一个元素的子列表
initial 返回除了第后个元素的子列表
append 添加一个元素到列表尾部
prepend 添加一个元素到列表头部
reverse 返回逆序的列表
uniq 移除列表中的重复项
without without LIST ITEM,返回剔除掉ITEM的列表
has has ITEM LIST,判断列表中是否存在ITEM
dict操作
set set DICT KEY VALUE,设置键值
unset unset DICT KEY,移除键值 
hasKey hasKey,判断是否存在键。示例:
1
{{- if not ( hasKey .Vars "ObjectStoreBucket" ) }}
pluck pluck KEY D1 D2,获取所有字典中指定的键的值 
keys keys D1 D2,返回所有字典的键的数组 
pick pick K1 K2 DICT,提取指定的键值,返回新的字典 

 

← 使用Gitea搭建Git服务器
使用sysrqd进行远程控制 →
4 Comments On This Topic
  1. 回复
    不归路
    2018/09/12

    您好,看到您的资料,十分有用,但有个问题,我现在解决不了,麻烦您帮忙看下:
    我将一个路径按“/”分隔后, 得到一个map,然后获取其最后一个,理论上得到长度再减1就是最后一个的索引,但在模板语言中,实在不知道怎么写,麻烦了

    • 回复
      Alex
      2018/09/12

      路径分隔后应该得到的是数组吧?用index函数就可以了

  2. 回复
    ygqygq2
    2018/11/02

    您好!我想问下,如果我有个变量是数字,我想让它变成可以循环使用的列表啥的。比如:
    replica: 3 # 当然数字可以自定义
    我想循环,0到3。得到想要的字符串:
    "test-0,test-1,test-2"

    • 回复
      Alex
      2018/11/05

      大概这样:
      import (
      "html/template"
      "github.com/Masterminds/sprig"
      "os"
      )

      func main() {
      tmplText := `{{ range $index, $element := until 3 }}test-{{ $index}},{{ end }}`
      tmpl, _ := template.New("test").Funcs(sprig.FuncMap()).Parse(tmplText)
      tmpl.Execute(os.Stdout, "1111")
      }

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • Kuberentes客户端编程
  • Go语言系统编程
  • Ginkgo学习笔记
  • Goland知识集锦
  • Protocol Buffers初探

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2