HTML渲染
【示例1】
首先定义一个存放模板文件的 templates
文件夹,然后在其内部按照业务分别定义一个 posts
文件夹和一个 users
文件夹。
posts/index.tmpl
html
{{define "posts/index.tmpl"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>posts/index</title>
</head>
<body>
{{.title}}
</body>
</html>
{{end}}
users/index.tmpl
html
{{define "users/index.tmpl"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>users/index</title>
</head>
<body>
{{.title}}
</body>
</html>
{{end}}
main.go
Gin框架中使用 LoadHTMLGlob()
或者 LoadHTMLFiles()
方法进行HTML模板渲染。
go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
// r.LoadHTMLFiles("templates/index.tmpl", "templates/users/index.tmpl") // 模板解析
r.LoadHTMLGlob("templates/**/*") // 从templates4目录及其所有子目录中加载所有的文件
r.GET("/posts/index", func(c *gin.Context) {
// HTTP请求
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{ // 模板渲染
"title": "posts/index.tmpl",
})
})
r.GET("/users/index", func(c *gin.Context) {
// HTTP请求
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ // 模板渲染
"title": "users/index.tmpl",
})
})
r.Run(":9090") // 启动server
}
效果
【示例2】
main.go
go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.LoadHTMLFiles("./index.tmpl") // 模板解析
r.GET("/index", func(c *gin.Context) {
// HTTP请求
c.HTML(http.StatusOK, "index.tmpl", gin.H{ // 模板渲染
"title": "liwenzhou.com",
})
})
r.Run(":9090") // 启动server
}
index.tmpl
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>posts/index</title>
</head>
<body>
{{.title}}
</body>
</html>
效果
自定义模板函数
定义一个不转义相应内容的 safe
模板函数
main.go
go
package main
import (
"github.com/gin-gonic/gin"
"html/template"
"net/http"
)
func main() {
r := gin.Default()
// gin框架中给模板添加自定义函数
r.SetFuncMap(template.FuncMap{
"safe": func(str string) template.HTML {
return template.HTML(str)
},
})
r.LoadHTMLGlob("templates/**/*") // 从templates4目录及其所有子目录中加载所有的文件
r.GET("/users/index", func(c *gin.Context) {
// HTTP请求
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ // 模板渲染
"title": "<a href='https://liwenzhou.com'>李文周的博客</a>",
})
})
r.Run(":9090") // 启动server
}
users/index.tmpl
go
{{define "users/index.tmpl"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>users/index</title>
</head>
<body>
{{.title | safe}}
</body>
</html>
{{end}}
效果
静态文件处理
当渲染的HTML文件中引用了静态文件时,只需要按照以下方式在渲染页面前调用 gin.Static
方法即可。
main.go
go
package main
import (
"github.com/gin-gonic/gin"
"html/template"
"net/http"
)
// 静态文件: html页面上用到的样式文件 。css js文件 图片
func main() {
r := gin.Default()
// 加载静态文件
r.Static("/xxx", "./statics")
// gin框架中给模板添加自定义函数
r.SetFuncMap(template.FuncMap{
"safe": func(str string) template.HTML {
return template.HTML(str)
},
})
r.LoadHTMLGlob("templates/**/*") // 从templates4目录及其所有子目录中加载所有的文件
r.GET("/users/index", func(c *gin.Context) {
// HTTP请求
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{ // 模板渲染
"title": "<a href='https://liwenzhou.com'>李文周的博客</a>",
})
})
r.Run(":9090") // 启动server
}
users/index.tmpl
html
{{define "users/index.tmpl"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="/xxx/index.css">
<title>users/index</title>
</head>
<body>
{{.title | safe}}
</body>
</html>
{{end}}
index.css
css
body {
background-color: cadetblue;
}
效果
使用模板继承
Gin框架默认都是使用单模板,如果需要使用 block template
功能,可以通过 "github.com/gin-contrib/multitemplate"
库实现,具体示例如下:
首先,假设项目目录下的templates文件夹下有以下模板文件,其中 home.tmpl
和 index.tmpl
继承了 base.tmpl
:
go
templates
├── includes
│ ├── home.tmpl
│ └── index.tmpl
├── layouts
│ └── base.tmpl
└── scripts.tmpl
定义一个 loadTemplates
函数
go
func loadTemplates(templatesDir string) multitemplate.Renderer {
r := multitemplate.NewRenderer()
layouts, err := filepath.Glob(templatesDir + "/layouts/*.tmpl")
if err != nil {
panic(err.Error())
}
includes, err := filepath.Glob(templatesDir + "/includes/*.tmpl")
if err != nil {
panic(err.Error())
}
// 为layouts/和includes/目录生成 templates map
for _, include := range includes {
layoutCopy := make([]string, len(layouts))
copy(layoutCopy, layouts)
files := append(layoutCopy, include)
r.AddFromFiles(filepath.Base(include), files...)
}
return r
}
main
函数
go
func indexFunc(c *gin.Context){
c.HTML(http.StatusOK, "index.tmpl", nil)
}
func homeFunc(c *gin.Context){
c.HTML(http.StatusOK, "home.tmpl", nil)
}
func main(){
r := gin.Default()
r.HTMLRender = loadTemplates("./templates")
r.GET("/index", indexFunc)
r.GET("/home", homeFunc)
r.Run()
}
补充文件路径处理
关于模板文件和静态文件的路径,需要根据公司/项目的要求进行设置。可以使用下面的函数获取当前执行程序的路径。
go
func getCurrentPath() string {
if ex, err := os.Executable(); err == nil {
return filepath.Dir(ex)
}
return "./"
}
JSON渲染
方法1:使用map
main.go
1 :map
go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/json", func(c *gin.Context) {
data := map[string]interface{}{
"name": "小王子",
"message": "hello world!",
"age": 18,
}
c.JSON(http.StatusOK, data)
})
r.Run(":9090")
}
main.go
2:gin.H
go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/json", func(c *gin.Context) {
data := gin.H{"name": "小王子", "message": "hello world!", "age": 18}
c.JSON(http.StatusOK, data)
})
r.Run(":9090")
}
效果
方法2: 结构体
main,go
go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
type msg struct {
Name string
Message string
Age int
}
r.GET("/another_json", func(c *gin.Context) {
data := msg{
"小王子",
"Hello golang!",
18,
}
c.JSON(http.StatusOK, data)
})
r.Run(":9090")
}
效果
使用tag
go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
type msg struct {
// 灵活使用tag来对结构体字段做定制化操作
Name string `json:"name"`
Message string
Age int
}
r.GET("/another_json", func(c *gin.Context) {
data := msg{
"小王子",
"Hello golang!",
18,
}
c.JSON(http.StatusOK, data)
})
r.Run(":9090")
}
XML渲染
注意需要使用具名的结构体类型。
go
func main() {
r := gin.Default()
// gin.H 是map[string]interface{}的缩写
r.GET("/someXML", func(c *gin.Context) {
// 方式一:自己拼接JSON
c.XML(http.StatusOK, gin.H{"message": "Hello world!"})
})
r.GET("/moreXML", func(c *gin.Context) {
// 方法二:使用结构体
type MessageRecord struct {
Name string
Message string
Age int
}
var msg MessageRecord
msg.Name = "小王子"
msg.Message = "Hello world!"
msg.Age = 18
c.XML(http.StatusOK, msg)
})
r.Run(":8080")
}
YMAL渲染
go
r.GET("/someYAML", func(c *gin.Context) {
c.YAML(http.StatusOK, gin.H{"message": "ok", "status": http.StatusOK})
})
protobuf渲染
go
r.GET("/someProtoBuf", func(c *gin.Context) {
reps := []int64{int64(1), int64(2)}
label := "test"
// protobuf 的具体定义写在 testdata/protoexample 文件中。
data := &protoexample.Test{
Label: &label,
Reps: reps,
}
// 请注意,数据在响应中变为二进制数据
// 将输出被 protoexample.Test protobuf 序列化了的数据
c.ProtoBuf(http.StatusOK, data)
})