【Golang】Go语言Web开发之模板渲染

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑

🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。

🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏: Go语言开发零基础到高阶实战
景天的主页: 景天科技苑

文章目录

Go语言中的模板渲染

在Go语言中,模板渲染是一种强大的工具,用于根据数据动态生成文本输出,特别是在Web开发中,模板渲染能够极大地方便HTML页面的生成。本文将结合实际案例,详细讲解Go语言中模板渲染的用法,包括模板的定义、解析、渲染以及模版语法等高级功能的使用。

一、模板渲染的基本概念

模板渲染是指通过模板引擎,将模板文件和数据结合起来,生成最终的文本输出。在Go语言中,标准库提供了text/templatehtml/template两个包,分别用于生成普通文本和安全的HTML输出。

  • text/template:用于生成普通文本输出。
  • html/template:用于生成安全的HTML输出,防止代码注入攻击。

二、模板文件的定义

模板文件通常是以.tmpl.tpl为后缀的文件,但也可以使用其他后缀。模板文件中包含占位符,用于在渲染时被数据替换。

示例模板文件(person.tmpl):

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Person Info</title>
</head>
<body>
    <h1>Person Info</h1>
    <p>Name: {{.Name}}</p>
    <p>Age: {{.Age}}</p>
</body>
</html>

三、模板的解析与渲染

1. 解析模板文件

使用template.ParseFilestemplate.Parse方法解析模板文件或模板字符串,生成模板对象。

示例代码:

go 复制代码
package main

import (
    "html/template"
    "os"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    // 解析模板文件
    tmpl, err := template.ParseFiles("./person.tmpl")
    if err != nil {
        panic(err)
    }

    // 创建数据
    data := Person{Name: "John Doe", Age: 30}

    // 执行模板渲染
    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
        panic(err)
    }
}

运行上述代码,将会在终端上看到如下输出:

2. 模版渲染及输出方式

在渲染时候需要传递一个参数,用于指定输出到何处,常见的有4种目的地:

▷ 输出到文件

▷ 输出到变量

▷ 输出到屏幕

▷ 输出到页面

(1)输出到文件
go 复制代码
package main

import (
	"html/template"
	"log"
	"os"
)

type Person9 struct {
	Name    string
	Age     int
	Hobbies []string
}

func main() {
	tmplStr := `  
<!DOCTYPE html>  
<html>  
<head>  
	<meta charset="UTF-8">  
	<title>Person Info</title>  
</head>  
<body>  
	<h1>Person Info</h1>  
	<p>Name: {{.Name}}</p>  
	<p>Age: {{.Age}}</p>  
	<ul>  
	{{range .Hobbies}}  
		<li>{{.}}</li>  
	{{end}}  
	</ul>  
</body>  
</html>  
`

	// 创建一个模板对象,并解析模板字符串
	tmpl, err := template.New("personTmpl").Parse(tmplStr)
	if err != nil {
		log.Fatalf("Error parsing template: %v", err)
	}

	// 创建一个Person实例,并填充数据
	p := Person9{
		Name:    "John Doe",
		Age:     30,
		Hobbies: []string{"Reading", "Hiking", "Cooking"},
	}

	// 打开一个文件用于写入生成的HTML内容
	file, err := os.Create("person_info.html")
	if err != nil {
		log.Fatalf("Error creating file: %v", err)
	}
	defer file.Close()

	// 将模板应用到数据,并将结果写入文件
	err = tmpl.Execute(file, p)
	if err != nil {
		log.Fatalf("Error executing template: %v", err)
	}

	log.Println("HTML file generated successfully: person_info.html")
}

查看输出的文件

(2)输出到变量
go 复制代码
var buf bytes.Buffer

if err := tmpl.Execute(&buf, foo); err != nil {
    log.Fatalln(err)
}
fmt.Println(buf.String())
(3)输出到屏幕

下方各个案例都是输出到屏幕

(4)输出到页面
go 复制代码
// http_server.go
package main

import (
	"fmt"
	"html/template"
	"net/http"
)

type User struct {
	Name string
	Age  int
}

// 定义发送接收数据函数
// 通过请求,进入页面(路由)  temp.Execute(resp, data)
// 通过URl进入某个页面
func findAll(resp http.ResponseWriter, req *http.Request) {
	// 接收到前端的信息 /findAll, 查询全部用户
	//使用map模拟用户
	userMap := make(map[int]User)
	userMap[1] = User{"jigntian", 1}
	userMap[2] = User{"xiaoming", 2}

	// 返回给前端页面并渲染上去
	// func ParseFiles(filenames ...string) (*Template, error)
	temp, _ := template.ParseFiles("./userlist.html")
	data := make(map[string]map[int]User)
	data["data"] = userMap
	//func (t *Template) Execute(wr io.Writer, data any) error
	// 输出到页面 第一个参数是 http.ResponseWriter 第二个参数是数据
	err := temp.Execute(resp, data)
	if err != nil {
		return
	}
}

func main() {
	// HandleFunc http请求的处理函数
	// http程序启动之后是不会停止的,一直监听请求
	// 访问这个url就会触发 helloHandler 函数 (Request) ResponseWriter
	// func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
	//第一个参数是请求路径,第二个参数是一个函数
	http.HandleFunc("/findAll", findAll)
	fmt.Println("Starting server at :8080")
	// func ListenAndServe(addr string, handler Handler) error
	// ListenAndServe监听TCP地址addr,并且会使用handler参数调用Serve函数处理接收到的连接。handler参数一般会设为nil,此时会使用DefaultServeMux。
	//如果用户自定义实现了Handler,那么根据相应路径在map中查询到相对应的Handler,然后再调用用户自定义的ServeHTTP处理请求。
	//如果用户没有自定义Handler,只注册了对应处理函数(使用了http.HandleFunc),那么就会根据默认DefaultServeMux去map查询到这个函数类型Handler,然后再调用ServeHTTP处理函数。
	// 开启监听程序的代码是放在main方法的最后一行的。
	if err := http.ListenAndServe("127.0.0.1:8080", nil); err != nil {
		fmt.Println("Error starting server:", err)
	}
}

userlist.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>查询用户</title>
</head>
<body>
<!--获取后端的数据 {{.data}}
遍历
{{range $k,$v := .data}}

{{end}}

-->

{{range $k,$v := .data}}
    {{$k}}
    {{if eq $k 1}}
        {{.Name}}
        {{$v}}
    {{end}}
{{end}}

</body>
</html>

运行服务器,浏览器访问

3. 从字符串载入模板

除了从文件载入模板,还可以从字符串载入模板。

示例代码:

go 复制代码
package main

import (
    "html/template"
    "os"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    // 定义模板字符串
    tmplStr := `
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Person Info</title>
</head>
<body>
    <h1>Person Info</h1>
    <p>Name: {{.Name}}</p>
    <p>Age: {{.Age}}</p>
</body>
</html>
`

    // 解析模板字符串
    tmpl, err := template.New("person").Parse(tmplStr)
    if err != nil {
        panic(err)
    }

    // 创建数据
    data := Person{Name: "John Doe", Age: 30}

    // 执行模板渲染
    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
        panic(err)
    }
}

四、模板语法

Go语言的模板语法简单而强大,支持变量、管道、条件判断、循环等控制结构。

1. 变量

使用{``{.}}表示当前数据对象,使用{``{$var := .}}可以定义一个变量,并在模板中通过$var引用它。

示例代码:

go 复制代码
package main

import (
    "html/template"
    "os"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    tmplStr := `
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Person Info</title>
</head>
<body>
    <h1>Person Info</h1>
    {{$name := .Name}}
    <p>Name: {{$name}}</p>
    <p>Age: {{.Age}}</p>
</body>
</html>
`

    tmpl, err := template.New("person").Parse(tmplStr)
    if err != nil {
        panic(err)
    }

    data := Person{Name: "John Doe", Age: 30}
    err = tmpl.Execute(os.Stdout, data)
    if err != nil {
        panic(err)
    }
}

2. 注释

在模板中,可以使用{{/* 注释内容 */}}来添加注释。

go 复制代码
package main  
  
import (  
    "fmt"  
    "html/template"  
)  
  
func main() {  
    // 定义模板字符串  
    tmplStr := `  
<p>Name: {{.Name}}</p>  
<!-- 这是一个注释 -->  
<p>Age: {{.Age}}</p>  
{{/* 这也是一个注释 */}}  
`  
  
    // 解析模板字符串(省略了其他部分)  
    // ...  
  
    // 创建数据(省略了其他部分)  
    // ...  
  
    // 执行模板渲染(省略了其他部分)  
    // ...  
}

3. 管道

管道符号|用于将前一个命令的输出作为后一个命令的输入。

示例代码:

go 复制代码
package main

import (
	"html/template"
	"os"
	"strings"
)

type Person4 struct {
	Name string
	Age  int
}

func main() {
	tmplStr := `  
<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="UTF-8">  
    <title>Person Info</title>  
</head>  
<body>
    <h1>Person Info</h1>  
    <p>Name: {{.Name | ToUpper}}</p>  {{/* 这里将我们定义的过滤器应用 */}}  
    <p>Age: {{.Age}}</p>  
</body>  
</html>  
`

	// 注册自定义函数
	funcMap := template.FuncMap{
		"ToUpper": strings.ToUpper,
	}

	// 将我们定义的函数应用于字符串解析
	tmpl, err := template.New("person").Funcs(funcMap).Parse(tmplStr)
	if err != nil {
		panic(err)
	}

	data := Person4{Name: "John Doe", Age: 30}
	err = tmpl.Execute(os.Stdout, data)
	if err != nil {
		panic(err)
	}
}

可见名字变成了大写

4. 条件判断

在模板中,可以使用{{if ...}} {{else if...}} {{else}} {{end}}来进行条件判断。

模板if常见操作符

• not 非{{if not .condition}} {{end}}

• and 与{{if and .condition1 .condition2}} {{end}}

• or 或{{if or .condition1 .condition2}} {{end}}

• eq 等于{{if eq .var1 .var2}} {{end}}

• ne 不等于{{if ne .var1 .var2}} {{end}}

• lt 小于 (less than){{if lt .var1 .var2}} {{end}}

• le 小于等于{{if le .var1 .var2}} {{end}}

• gt 大于{{if gt .var1 .var2}} {{end}}

• ge 大于等于{{if ge .var1 .var2}} {{end}}

示例代码:

go 复制代码
package main

import (
	"html/template"
	"os"
)

type Person6 struct {
	Name string
	Age  int
}

func main() {
	tmplStr := `  
<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="UTF-8">  
    <title>Person Info</title>  
</head>  
<body>  
    <h1>Person Info</h1>  
    <p>Name: {{.Name}}</p>  
    <p>Age: {{.Age}}</p>  
    {{if lt .Age  18}}  
        <p>This person is a boy.</p>  
    {{else if  gt .Age 60 }}  
		<p>This person is not an old man.</p> 
	{{else}}
		<p>This person is not an adult.</p>
    {{end}}  
</body>  
</html>  
`

	tmpl, err := template.New("person6").Parse(tmplStr)
	if err != nil {
		panic(err)
	}

	data := Person6{Name: "John Doe", Age: 70}
	err = tmpl.Execute(os.Stdout, data)
	if err != nil {
		panic(err)
	}
}

5. 循环

在模板中,可以使用{{range ...}} {{end}}来进行循环。

示例代码:

go 复制代码
package main

import (
	"html/template"
	"os"
)

type Person7 struct {
	Name string
	Age  int
}

type People8 struct {
	List []Person7
}

func main() {
	// 定义模板字符串
	tmplStr := `  
<ul>  
{{range .List}}  
    <li>Name: {{.Name}}, Age: {{.Age}}</li>  
{{end}}  
</ul>  
`

	tmpl, err := template.New("person").Parse(tmplStr)
	if err != nil {
		panic(err)
	}

	// 创建数据
	data := People8{
		List: []Person7{
			{Name: "jingtian", Age: 25},
			{Name: "John Doe", Age: 30},
			{Name: "Jane Smith", Age: 25},
		},
	}
	err = tmpl.Execute(os.Stdout, data)
	if err != nil {
		panic(err)
	}
}
相关推荐
ekskef_sef10 分钟前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
飞飞-躺着更舒服15 分钟前
【QT】实现电子飞行显示器(改进版)
开发语言·qt
武昌库里写JAVA31 分钟前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计
sunshine64134 分钟前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻1 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云1 小时前
npm淘宝镜像
前端·npm·node.js
dz88i81 小时前
修改npm镜像源
前端·npm·node.js
Jiaberrr1 小时前
解锁 GitBook 的奥秘:从入门到精通之旅
前端·gitbook
ZSYP-S1 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos1 小时前
c++------------------函数
开发语言·c++