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. 第五篇--VS2017如何生成Dll文件

    参考资料: https://blog.csdn.net/qq_34097715/article/details/79540933 https://www.cnblogs.com/RascallySna ...

  2. 【剑指offer】53 - II. 0~n-1中缺失的数字

    剑指 Offer 53 - II. 0-n-1中缺失的数字 知识点:数组,二分查找: 题目描述 统计一个数字在排序数组中出现的次数. 示例 输入: nums = [5,7,7,8,8,10], tar ...

  3. 用python将word转pdf、doc转docx等

    word ==> pdf def doc2pdf(file_path): """ word格式转换doc|docx ==> pdf :return: &quo ...

  4. [JS]回调函数和回调地狱

    回调函数 小明在奶茶店点了奶茶,店员开始制作奶茶,此时"制作奶茶"与"小明等待奶茶"是一个同时进行的不同的两个事件(任务),那么,小明获取店员制作成功的奶茶是从 ...

  5. node溢出

    在做项目的过程中,项目越来越大,后面导致项目无法正常启动,查了原因是因为node 溢出了. 先看看溢出时报的错 解决办法:  increase-memory-limit插件 1.在package.js ...

  6. Android NDK 直播推流与引流

    本篇介绍一下直播技术中推流与引流的简单实现. 1.流媒体服务器测试 首先利用快直播 app (其他支持 RTMP 推流与引流的 app 亦可)和 ffplay.exe 对流媒体服务器进行测试. 快直播 ...

  7. VBA·Function的基础使用

    阅文时长 | 0.27分钟 字数统计 | 440字符 主要内容 | 1.引言&背景 2.基本结构 3.Demo示例 4.声明与参考资料 『VBA·Function的基础使用』 编写人 | SC ...

  8. Sqli-Labs less25-25a

    less-25 前置基础知识:后面的关卡涉及到WAF绕过: 主要有三种方式:白盒绕过.黑盒绕过.fuzz测试 网上sql注入WAF绕过的教程有很多,可以自己查询,总之就是比谁思路猥琐 根据第25关下面 ...

  9. java JNI介绍

    java JNI介绍 目录 java JNI介绍 1. Java调用C++代码 2.C++代码调用java代码 JNI是Java Native Interface的全称. oracle文档中是这样描述 ...

  10. luogu P6239 奇怪的道路

    奇怪的道路 我看不出来是状压的状压 好吧,其实看到k的范围应该去往状压方面想的. 然后,题目中说"任何一个城市都与恰好偶数条道路相连(0也被认为是偶数)". 所以,奇偶,两种状态可 ...