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. 以两种异步模型应用案例,深度解析Future接口

    摘要:本文以实际案例的形式分析了两种异步模型,并从源码角度深度解析Future接口和FutureTask类. 本文分享自华为云社区<[精通高并发系列]两种异步模型与深度解析Future接口(一) ...

  2. Linux 查看内存命令

    Linux 查看内存命令 top命令, Linux的top命令提供Linux资源使用情况的实时更新信息.不仅可以查看Linux内存,也可以查看CPU以及各个进程之间的对资源的占用情况.使用方式如下: ...

  3. Centos 7 安装mysql5.7 nginx tomcat

  4. 【开源】这可能是封装微信 API 最全的 .NET SDK 了

    ## 缘起 今年公司某个项目需要全面接入微信支付 V3 版 API.起初觉得,2014 年微信支付就已上线了 V3 版 API,这都 2021 年了,就算官方不给力,怎么着社区也该有几个造好的 .NE ...

  5. 官宣 .NET MAUI 预览版 6

    最近,我们发布了.NET 多平台应用程序UI (MAUI)的最新进展. 在此版本中,我们全力支持Visual Studio 2022 预览版2.这也标志着我们首次将.NET MAUI 作为工作负载安装 ...

  6. 线程强制执行_join

    线程强制执行_join Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞 可以想象为插队 测试案例: package multithreading; // 测试Join方法 // 想 ...

  7. WPF自定义控件三:消息提示框

    需求:实现全局消息提示框 一:创建全局Message public class Message { private static readonly Style infoStyle = (Style)A ...

  8. [06 Go语言基础-包]

    [06 Go语言基础-包] 包 什么是包,为什么使用包? 到目前为止,我们看到的 Go 程序都只有一个文件,文件里包含一个 main 函数和几个其他的函数.在实际中,这种把所有源代码编写在一个文件的方 ...

  9. 深入理解HTTP请求流程

    以前写过一篇博客,叫做HTTP的报文分析:https://blog.csdn.net/ZripenYe/article/details/119593269但是感觉还是不太深入.不够全面,顶多了解个大概 ...

  10. 安全工具推荐之HackTools插件

    朋友推荐 链接:https://github.com/LasCC/Hack-Tools 一款多合一Chromium类红队浏览器插件,火狐也有对应版本 功能包括: 动态反向Shell生成器(PHP.Ba ...