Golang-Web(HTMl模板和静态资源)

一. 项目结构

  • 在Go语言中web项目标准结构如下

--项目名
    --src
    --static
        --css
        --images
        --js
    --view
        --index.html
    --main.go

 

  • Go语言标准库中html/template包提供了html模版支持,把HTML当作模版可以在访问控制器时显示HTML模版信息

    • 这也符合标准的MVC思想

二.HTML模版显示

  • 使用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代码如下

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
    <!--路径以斜杠开头,表示项目根目录下-->
    <script type="text/javascript" src="/static/js/index.js"></script>
</head>
<body>
    这是要显示的html页面信息<br/>
    <button onclick="myclick()">按钮</button>
</body>
</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中使用{{.}}获取传递数据即可.所有基本类型都是使用此方式进行传递

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
<pre>
尊敬的{{.}}先生/女士
    您已经被我公司录取,收到此消息后请您仔细阅读附件中"注意事项"
    再次祝您:{{.}}好运
</pre>
</body>
</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, "smallming") //此处传递数据
}
​
func main() {
    server := http.Server{Addr: ":8090"}
    http.HandleFunc("/", welcome)
    server.ListenAndServe()
}

 传递结构体类型数据

  • 结构体的属性首字母必须大写才能被模版访问

  • 在模版中直接使用{{.属性名}}获取结构体的属性

  • HTML代码如下

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
<pre>
当前登录用户信息:<br/>
    姓名:{{.Name}}<br/>
    年龄:{{.Age}}
</pre>
</body>
</html>
  • 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()
}
 

向模版传递map类型数据

  • 直接使用{{.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代码如下,里面使用了连缀写法

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
<pre>
当前登录用户信息:<br/>
    姓名:{{.user.Name}}<br/>
    年龄:{{.user.Age}}<br/>
    购物金额:{{.money}}
</pre>
</body>
</html>

调用方法

  • 在模版中调用函数时,如果是无参函数直接调用函数名即可,没有函数的括号

  • 例如在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代码如下

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
<pre>
    <!--调用time变量的无参方法-->
    取出时间中的年:{{.Year}} <br/>
    取出时间中的年:{{.Month}} <br/>
    <!--调用有参数方法-->
    格式化后的内容:{{.Format "2006-01-02"}}
</pre>
</body>
</html>

调用自定义函数/方法

  • 如果希望调用自定义函数,需要借助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代码示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    传递过来的时间:{{.}}<br/>
    调用自定义函数,格式化后的时间:{{mf .}}<br/>
</body>
</html>

五. Action

  • 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 使用

  • 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文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>if测试</title>
</head>
<body>
测试if是否执行<br/>
{{if . }}
if成立这个位置输出
{{end}}
</body>
</html>
  • 直接在HTMl中定义变量演示if..else用法(go文件不变)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试</title>
</head>
<body>
{{$n:=123}}
{{if ne $n 123}}
if成立这个位置输出
{{else}}{{/* 比if结构多了else */}}
这是else的功能
{{end}}
</body>
</html>
  • go文件不变,演示if...else if...else用法

<body>
{{$n:=124}}
{{if eq $n 123}}
123
{{else if eq $n 124}}
124
{{else if eq $n 125}}
125
{{else}}
else
{{end}}
</body>
  • 在模版中也可以相互嵌套

<body>
{{$n:=124}}
{{if gt $n 100}}
    {{if gt $n 200}}
        gt 200
    {{else}}
        lt 200
    {{end}}
{{else}}
    小于100
{{end}}
</body>

range使用

  • range遍历数组或切片或map或channel时,在range内容中{{.}}表示获取迭代变量


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<script type="text/javascript" src="/static/js/myclick.js">
</script>
<body>
获取map数据<br/>
    {{range .}}
    {{.}}{{/* 此处dot为迭代变量 */}}<br/>
    {{end}}
</body>
</html>

后台传值

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"}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
{{template "head" }}<br/>
中间的内容<br/>
{{template "foot" }}
</body>
</html>
{{end}}
{{define "head"}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
head.html
</body>
</html>
{{end}}
{{define "foot"}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
    foot.html
</body>
</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标签的<iframe>,但是要动态效果时,可以在调用模版给模版传递参数

{{define "layout"}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
{{template "head" "head的参数"}}<br/>
中间的内容<br/>
{{template "foot" "foot的参数"}}
</body>
</html>
{{end}}
  • 在子模版中依然是使用{{.}}获取传递过来的参数

{{define "head"}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
head.html
{{.}}
</body>
</html>
{{end}}

 

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值