在Go语言中web项目标准结构如下
--项目名 --src --static --css --images --js --view --index.html --main.go
Go语言标准库中html/template包提供了html模版支持,把HTML当作模版可以在访问控制器时显示HTML模版信息
这也符合标准的MVC思想
使用template.ParseFiles()可以解析多个模版文件
// ParseFiles creates a new Template and parses the template definitions from
// the named files. The returned template's name will have the (base) name and
// (parsed) contents of the first file. There must be at least one file.
// If an error occurs, parsing stops and the returned *Template is nil.
//
// When parsing multiple files with the same name in different directories,
// the last one mentioned will be the one that results.
// For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
// named "foo", while "a/foo" is unavailable.
func ParseFiles(filenames ...string) (*Template, error) {
return parseFiles(nil, filenames...)
}
把模版信息响应写入到输出流中
// Execute applies a parsed template to the specified data object,
// writing the output to wr.
// If an error occurs executing the template or writing its output,
// execution stops, but partial results may already have been written to
// the output writer.
// A template may be executed safely in parallel, although if parallel
// executions share a Writer the output may be interleaved.
func (t *Template) Execute(wr io.Writer, data interface{}) error {
if err := t.escape(); err != nil {
return err
}
return t.text.Execute(wr, data)
}
代码演示,显示index.html信息
因为配置的pattern为"/"所以资源路径任意,都可以访问到这个HTML
package main
import (
"net/http"
"html/template"
)
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
t.Execute(w, nil) //第二个参数表示向模版传递的数据
}
func main() {
server := http.Server{Addr: ":8090"}
http.HandleFunc("/", welcome)
server.ListenAndServe()
}
把静态文件放入到特定的文件夹中,使用Go语言的文件服务就可以进行加载
项目结构
--项目 --static --js --index.js --view --index.html --main.go
index.html代码如下
Title
这是要显示的html页面信息
index.js
function myclick(){
alert("您点击了按钮")
}
代码示例
package main
import (
"net/http"
"html/template"
)
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
t.Execute(w, nil) //第二个参数表示向模版传递的数据
}
func main() {
server := http.Server{Addr: ":8090"}
/*
访问url以/static/开头,就会把访问信息映射到指定的目录中
*/
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.HandleFunc("/", welcome)
server.ListenAndServe()
}
可以在HTML中使用{{}}获取template.Execute()第二个参数传递的值
最常用的{{.}}
中的"."是指针,指向当前变量,称为"dot"
在{{}}可以有的Argument,官方给定如下
- go语法的布尔值、字符串、字符、整数、浮点数、虚数、复数,视为无类型字面常数,字符串不能跨行 - 关键字nil,代表一个go的无类型的nil值 - 字符'.'(句点,用时不加单引号),代表dot的值 - 变量名,以美元符号起始加上(可为空的)字母和数字构成的字符串,如:$piOver2和$; 执行结果为变量的值,变量参见下面的介绍 - 结构体数据的字段名,以句点起始,如:.Field; 执行结果为字段的值,支持链式调用:.Field1.Field2; 字段也可以在变量上使用(包括链式调用):$x.Field1.Field2; - 字典类型数据的键名;以句点起始,如:.Key; 执行结果是该键在字典中对应的成员元素的值; 键也可以和字段配合做链式调用,深度不限:.Field1.Key1.Field2.Key2; 虽然键也必须是字母和数字构成的标识字符串,但不需要以大写字母起始; 键也可以用于变量(包括链式调用):$x.key1.key2; - 数据的无参数方法名,以句点为起始,如:.Method; 执行结果为dot调用该方法的返回值,dot.Method(); 该方法必须有1到2个返回值,如果有2个则后一个必须是error接口类型; 如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误; 方法可和字段、键配合做链式调用,深度不限:.Field1.Key1.Method1.Field2.Key2.Method2; 方法也可以在变量上使用(包括链式调用):$x.Method1.Field; - 无参数的函数名,如:fun; 执行结果是调用该函数的返回值fun();对返回值的要求和方法一样;函数和函数名细节参见后面。 - 上面某一条的实例加上括弧(用于分组) 执行结果可以访问其字段或者键对应的值: print (.F1 arg1) (.F2 arg2) (.StructValuedMethod "arg").Field
向HTML传递字符串数据.在HTML中使用{{.}}获取传递数据即可.所有基本类型都是使用此方式进行传递
Title
尊敬的{{.}}先生/女士
您已经被我公司录取,收到此消息后请您仔细阅读附件中"注意事项"
再次祝您:{{.}}好运
package main
import (
"net/http"
"html/template"
)
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
t.Execute(w, "smallming") //此处传递数据
}
func main() {
server := http.Server{Addr: ":8090"}
http.HandleFunc("/", welcome)
server.ListenAndServe()
}
结构体的属性首字母必须大写才能被模版访问
在模版中直接使用{{.属性名}}
获取结构体的属性
HTML代码如下
Title
当前登录用户信息:
姓名:{{.Name}}
年龄:{{.Age}}
go文件代码如下
User.go
package entity
//注意:只有首字母大写的属性才能在模版中访问到
type User struct {
Name string
Age int
}
package main
import (
"demo/entity"
"html/template"
"net/http"
)
/*
HTML模板和静态资源
*/
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
user:=new(entity.User)
user.Name="smallming"
user.Age=18
//t.Execute(w, entity.User{Name:"小明",Age:18}) //第二个参数表示向模版传递的数据
t.Execute(w,user)
}
func main() {
server := http.Server{Addr: "192.168.12.37:8090"}
/*
访问url以/static/开头,就会把访问信息映射到指定的目录中
*/
http.Handle("/static/", http.StripPrefix("/static/",
http.FileServer(http.Dir("static"))))
http.HandleFunc("/", welcome)
server.ListenAndServe()
}
直接使用{{.key}}
获取map中数据
模版中支持连缀写法(不仅仅是map)
go文件代码如下
package main
import (
"net/http"
"html/template"
)
//注意:只有首字母大写的属性才能在模版中访问到
type User struct {
Name string
Age int
}
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
m := make(map[string]interface{})
m["user"] = User{"张三", 20}
m["money"] = 998
t.Execute(w, m) //此处传递数据
}
func main() {
server := http.Server{Addr: ":8090"}
http.HandleFunc("/", welcome)
server.ListenAndServe()
}
HTML代码如下,里面使用了连缀写法
Title
当前登录用户信息:
姓名:{{.user.Name}}
年龄:{{.user.Age}}
购物金额:{{.money}}
在模版中调用函数时,如果是无参函数直接调用函数名即可,没有函数的括号
例如在go源码中时间变量.Year()
在模版中{{时间.Year}}
在模版中调用有参函数时参数和函数名称之间有空格,参数和参数之间也是空格
给定go文件代码
package main
import (
"net/http"
"html/template"
"time"
)
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
time:=time.Date(2018,1,2,3,4,5,0,time.Local)
t.Execute(w, time) //此处传递数据
}
func main() {
server := http.Server{Addr: ":8090"}
http.HandleFunc("/", welcome)
server.ListenAndServe()
}
html代码如下
Title
取出时间中的年:{{.Year}}
取出时间中的年:{{.Month}}
格式化后的内容:{{.Format "2006-01-02"}}
如果希望调用自定义函数,需要借助html/template包下的FuncMap进行映射
FuncMap本质就是map的别名type FuncMap map[string]interface{}
函数被添加映射后,只能通过函数在FuncMap中的key调用函数
go文件代码示例
package main
import (
"net/http"
"html/template"
"time"
)
//把传递过来的字符串时间添加一分钟后返回字符串格式时间
func MyFormat(s string) string{
t,_:=time.Parse("2006-01-02 15:04:05",s)
t=t.Add(60e9)//在时间上添加1分钟
return t.Format("2006-01-02 15:04:05")
}
func html(res http.ResponseWriter, req *http.Request) {
//把自定义函数绑定到FuncMap上
funcMap:=template.FuncMap{"mf":MyFormat}
//此处注意,一定要先绑定函数
t:=template.New("demo.html").Funcs(funcMap)
//绑定函数后在解析模版
t, _ = t.ParseFiles("demo.html")
s:="2009-08-07 01:02:03"
t.Execute(res, s)
}
func main() {
server := http.Server{
Addr: "localhost:8090",
}
http.HandleFunc("/html", html)
server.ListenAndServe()
}
HTML代码示例
Title
传递过来的时间:{{.}}
调用自定义函数,格式化后的时间:{{mf .}}
Go语言官方文档给出action(动作)的列表。"Arguments"和"pipelines"代表数据的执行结果
{{/* a comment */}}
注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止,就像这里表示的一样。
{{pipeline}}
pipeline的值的默认文本表示会被拷贝到输出里。
{{if pipeline}} T1 {{end}}
如果pipeline的值为empty,不产生输出,否则输出T1执行结果。不改变dot的值。
Empty值包括false、0、任意nil指针或者nil接口,任意长度为0的数组、切片、字典。
{{if pipeline}} T1 {{else}} T0 {{end}}
如果pipeline的值为empty,输出T0执行结果,否则输出T1执行结果。不改变dot的值。
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
用于简化if-else链条,else action可以直接包含另一个if;等价于:
{{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
{{range pipeline}} T1 {{end}}
pipeline的值必须是数组、切片、字典或者通道。
如果pipeline的值其长度为0,不会有任何输出;
否则dot依次设为数组、切片、字典或者通道的每一个成员元素并执行T1;
如果pipeline的值为字典,且键可排序的基本类型,元素也会按键的顺序排序。
{{range pipeline}} T1 {{else}} T0 {{end}}
pipeline的值必须是数组、切片、字典或者通道。
如果pipeline的值其长度为0,不改变dot的值并执行T0;否则会修改dot并执行T1。
{{template "name"}}
执行名为name的模板,提供给模板的参数为nil,如模板不存在输出为""
{{template "name" pipeline}}
执行名为name的模板,提供给模板的参数为pipeline的值。
{{with pipeline}} T1 {{end}}
如果pipeline为empty不产生输出,否则将dot设为pipeline的值并执行T1。不修改外面的dot。
{{with pipeline}} T1 {{else}} T0 {{end}}
如果pipeline为empty,不改变dot并执行T0,否则dot设为pipeline的值并执行T1。
action主要完成流程控制、循环、模版等操作.通过使用action可以在模版中完成简单逻辑处理(复杂逻辑处理应该在go中实现,传递给模版的数据应该是已经加工完的数据)
if写在模版中和写在go文件中功能是相同的,区别是语法
布尔函数会将任何类型的零值视为假,其余视为真。
if后面的表达式中如果包含逻辑控制符在模版中实际上是全局函数
and
函数返回它的第一个empty参数或者最后一个参数;
就是说"and x y"等价于"if x then y else x";所有参数都会执行;
or
返回第一个非empty参数或者最后一个参数;
亦即"or x y"等价于"if x then x else y";所有参数都会执行;
not
返回它的单个参数的布尔值的否定
len
返回它的参数的整数类型长度
index
执行结果为第一个参数以剩下的参数为索引/键指向的值;
如"index x 1 2 3"返回x[1][2][3]的值;每个被索引的主体必须是数组、切片或者字典。
print
即fmt.Sprint
printf
即fmt.Sprintf
println
即fmt.Sprintln
html
返回其参数文本表示的HTML逸码等价表示。
urlquery
返回其参数文本表示的可嵌入URL查询的逸码等价表示。
js
返回其参数文本表示的JavaScript逸码等价表示。
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则返回真
简单if示例-go文件
package main
import (
"net/http"
"html/template"
)
func test(rw http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("template/html/if.html")
//第二个参数传递类型默认值:nil,"",0,false等都会导致if不成立
t.Execute(rw, "")
}
func main() {
//创建server服务
server := http.Server{Addr: ":8090"}
//设置处理器函数
http.HandleFunc("/test", test)
//监听和开始服务
server.ListenAndServe()
}
简单if示例-html文件
if测试
测试if是否执行
{{if . }}
if成立这个位置输出
{{end}}
直接在HTMl中定义变量演示if..else用法(go文件不变)
测试
{{$n:=123}}
{{if ne $n 123}}
if成立这个位置输出
{{else}}{{/* 比if结构多了else */}}
这是else的功能
{{end}}
go文件不变,演示if...else if...else用法
{{$n:=124}}
{{if eq $n 123}}
123
{{else if eq $n 124}}
124
{{else if eq $n 125}}
125
{{else}}
else
{{end}}
在模版中也可以相互嵌套
{{$n:=124}}
{{if gt $n 100}}
{{if gt $n 200}}
gt 200
{{else}}
lt 200
{{end}}
{{else}}
小于100
{{end}}
range遍历数组或切片或map或channel时,在range内容中{{.}}表示获取迭代变量
Title
获取map数据
{{range .}}
{{.}}{{/* 此处dot为迭代变量 */}}
{{end}}
后台传值
package main
import (
"demo/entity"
"html/template"
"net/http"
)
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
user:=new(entity.User)
user.Name="smallming"
user.Age=18
strings := make(map[string]string);
strings["1"]="我是1"
strings["2"]="我是2"
strings["3"]="我是3"
//t.Execute(w, entity.User{Name:"小明",Age:18}) //第二个参数表示向模版传递的数据
t.Execute(w,strings)
}
func main() {
server := http.Server{Addr: "192.168.12.37:8090"}
/*
访问url以/static/开头,就会把访问信息映射到指定的目录中
*/
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.HandleFunc("/", welcome)
server.ListenAndServe()
}
页面显示
在实际项目中经常出现页面复用的情况,例如:整个网站的头部信息和底部信息复用
可以使用动作{{template "模版名称"}}引用模版
引用的模版必须在HTML中定义这个模版
{{define "名称"}}
html
{{end}}
执行主模版时也要给主模版一个名称,执行时调用的是ExecuteTemplate()方法
{{define "layout"}}
Title
{{template "head" }}
中间的内容
{{template "foot" }}
{{end}}
{{define "head"}}
Title
head.html
{{end}}
{{define "foot"}}
Title
foot.html
{{end}}
package main
import (
"net/http"
"html/template"
)
func welcome(w http.ResponseWriter, r *http.Request) {
//要加载所有需要被嵌套的文件
t, _ := template.ParseFiles("view/index.html", "view/head.html", "view/foot.html")
//执行主模版,主要调用的方法
t.ExecuteTemplate(w, "layout", nil)
}
func main() {
server := http.Server{Addr: ":8090"}
http.HandleFunc("/", welcome)
server.ListenAndServe()
}
如果直接引用html可以直接使用html标签的,但是要动态效果时,可以在调用模版给模版传递参数
{{define "layout"}}
Title
{{template "head" "head的参数"}}
中间的内容
{{template "foot" "foot的参数"}}
{{end}}
在子模版中依然是使用{{.}}获取传递过来的参数
{{define "head"}}
Title
head.html
{{.}}
{{end}}