一,Gin介绍
Gin是一个 Go (Golang) 编写的轻量级 http web 框架,运行速度非常快,如果你是性能和高效的追求者,我们推荐你使用Gin框架。
Gin最擅长的就是Api接口的高并发,如果项目的规模不大,业务相对简单,这个时候我们也推荐你使用 Gin.
当某个接口的性能遭到较大挑战的时候,这个还是可以考虑使用Gin重写接口。
Gin 也是一个流行的 golang Web 框架,Github Start 量已经超过了50k.
Gin的官网:
Gin的官网:
https://gin-gonic.com/zh-cn/
Gin Github地址:
https://github.com/gin-gonic/gin
二,Gin环境搭建
要安装 Gin 软件包,需要先安装 Go并设置 Go 工作区。
1,下载并安装 gin:
go get -u github.com/gin-gonic/gin
https://github.com/gin-gonic/gin
go install github.com/gin-gonic/gin@latest
go mod init gindemo01
go mod init Administrator
go mod tidy
go get -u github.com/gin-gonic/gin
2,将 gin 引入到代码中:
import "github.com/gin-gonic/gin"
3,(可选) 如果使用诸如 http.StatusOK之类的常量,则需要引入 net/http 包:
import "net/http"
4,新建 Main.go 配置路由
package main
import main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 配置路由
r.GET("/",func(c *gin.Context) {
c.JSON(200,gin.H{ // c.JSON:返回JSON格式的数据
"message":"Hello world!",
})
})
// 启动 HTTP 服务,默认在 0.0.0.0:8080 启动服务
r.Run()
}
5,运行你的项目
go run main.go
PS G:\GO\gin\gindemo01> go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET / --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2025/01/06 - 10:29:16 | 200 | 548.6µs | ::1 | GET "/"
[GIN] 2025/01/06 - 10:29:16 | 404 | 0s | ::1 | GET "/favicon.ico"
[GIN] 2025/01/06 - 10:32:48 | 404 | 0s | ::1 | GET "/new"
// 改完代码以后要重新运行
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2025/01/06 - 10:34:50 | 200 | 0s | ::1 | GET "/"
[GIN] 2025/01/06 - 10:34:53 | 200 | 0s | ::1 | GET "/new"
6,要改变默认启动的端口
r.Run(":9000")
如果 go get 失败请参考:
http://bbs.itying.com/topic/Sed08edee7c0790f8475e276
三,golang程序的热加载
所谓热加载就是当我们对代码进行修改时,程序能够自动重新加载并执行,这在我们开发中是非常便利的,可以快速进行代码测试,省去了每次手动重新编译
beego中我们可以使用官方给我们提供的bee工具来热加载项目,但是gin中并没有官方提供的热加载工具,这个时候我们要实现热加载就可以借助第三方的工具。
工具1 (推荐)
https://github.com/gravityblast/fresh
go install github.com/gravityblast/fresh@latest
go get github.com/pilu/fresh
G:/GO/gin_demo>fresh
工具2:
https://github.com/codegangsta/gin
go get -u github.com/codegangsta/gin
G:/GO/gin_demo>gin run main.go
四,Gin框架中的路由
4.1,路由概述
路由 (Routing) 是由一个 URI (或者叫路径) 和一个特定的 HTTP方法(GET,POST等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。
RESTful API 是目前比较成熟的一套互联网应用程序的API设计理论,所以我们设计我们的路由的时候建议参考 RESTful API 指南。
在 RESTful 架构中,每个网址代表一种资源,不同的请求方式表示执行不同的操作:
GET(SELECT) | 从服务器取出资源(一项或多项) |
---|---|
POST(CREATE) | 在服务器新建一个资源 |
PUT(UPDATE) | 在服务器更新资源(客户端提供改变后的完整资源) |
DELETE(DELETE) | 从服务器删除资源 |
4.2,简单的路由配置
简单的路由配置(可以通过 postman 测试)
当用GET请求访问一个网址的时候,做什么事情:
路由配置实践:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 配置路由 绑定路由规则,执行的函数
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "值:%v", "您好gin")
})
r.GET("/news", func(c *gin.Context) {
c.String(http.StatusOK, "我是新闻页面 111")
})
// r.GET("/about", func(c *gin.Context) {
// c.String(200, "我是关于页面 222")
// })
// r.GET("/login", func(c *gin.Context) {
// c.String(200, "这是一个登录页面")
// })
// r.GET("/register", func(c *gin.Context) {
// c.String(200, "这是一个注册页面")
// })
// r.GET("/user/:name", func(c *gin.Context) {
// name := c.Param("name")
// c.String(200, "这是一个用户页面 %s", name)
// })
// r.GET("/user/:name/:age", func(c *gin.Context) {
// name := c.Param("name")
// age := c.Param("age")
// c.String(200, "这是一个用户页面 %s %s", name, age)
// })
// r.GET("/user/:name/*action", func(c *gin.Context) {
// name := c.Param("name")
// action := c.Param("action")
// c.String(200, "这是一个用户页面 %s %s", name, action)
// })
// r.GET("/favicon.ico", func(c *gin.Context) {
// c.String(200, "这是一个图标页面")
// })
// r.GET("/favicon.ico", func(c *gin.Context) {
// c.String(200, "这是一个图标页面2")
// })
r.POST("/add", func(c *gin.Context) {
c.String(http.StatusOK, "这是一个post请求 主要用于增加数据")
// c.JSON(200, gin.H{
// "name": "小明",
// })
})
r.PUT("/edit", func(c *gin.Context) {
c.String(http.StatusOK, "这是一个put请求 主要用于编辑数据")
})
r.DELETE("/delete", func(c *gin.Context) {
c.String(http.StatusOK, "这是一个delete请求 主要用于删除数据")
})
//启动一个web服务 监听并在 0.0.0.0:8080 上
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
//r.Run(":8000") // 指定端口启动服务
}
4.3,c.String() c.JSON() c.JSONP() c.XML() c.HTML()
返回一个字符串
r.GET("/news",fnuc(c *gin.Context) {
aid := c.Query("aid")
c.String(200,"aid=%s",aid)
})
返回一个 JSON 数据
func main() {
r := gin.Default()
// gin.H 是 map[string]interface{}的缩写
r.GET("/someJSON", func(c *gin.Context) {
// 方式一: 自己拼接JSON
c.JSON(http.StatusOK,gin.H{"message": "Hello world!"})
})
r.GET("/moreJSON",func(c *gin.Context) {
// 方式二:使用结构体
var msg struct {
Name string `json:"user"`
}
})
}
新建项目实践:
1,初始化项目
PS G:\gin\gindemo02> go mod init gindemo02
go: creating new go.mod: module gindemo02
go: to add module requirements and sums:
go mod tidy
PS G:\gin\gindemo02> go mod tidy
PS G:\gin\gindemo02> dir
目录: G:\gin\gindemo02
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2025-01-06 17:18 28 go.mod
-a---- 2025-01-06 17:14 36 main.go
2,下载 gin 框架
PS G:\gin\gindemo02> go get -u github.com/gin-gonic/gin
PS G:\gin\gindemo02> dir
目录: D:\gin\gindemo02
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2025-01-06 17:56 1376 go.mod
-a---- 2025-01-06 17:56 6940 go.sum
-a---- 2025-01-06 17:48 267 main.go
3,下载 第三方 热加载工具 fresh
https://github.com/gravityblast/fresh
go install github.com/gravityblast/fresh@latest
go install github.com/gravityblast/fresh@latest
go get github.com/pilu/fresh
G:/gin_demo>fresh
返回一个 JSON 数据实践:
package main
import "github.com/gin-gonic/gin"
// 定义一个结构体
type Article struct {
Title string `json:"title"`
Description string `json:"description"`
Content string `json:"content"`
}
// type Article struct {
// ID int `json:"id"`
// Title string `json:"title"`
// Content string `json:"content"`
// }
// type Login struct {
// Username string `json:"username"`
// Password string `json:"password"`
// }
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(200, "值:%v", "首页")
})
// 返回json数据
r.GET("/json1", func(c *gin.Context) {
c.JSON(200, map[string]interface{}{
"success": true,
"msg": "您好gin",
})
})
// 另一种写法 gin.H 就是 map[string]interface{}
r.GET("/json2", func(c *gin.Context) {
c.JSON(200, gin.H{
"success": true,
"msg": "您好gin,golang",
})
})
// 结构体返回json数据
r.GET("/json3", func(c *gin.Context) {
a := &Article{
Title: "gin入门",
Description: "gin入门教程",
Content: "gin入门教程内容",
}
c.JSON(200, a)
})
// r.GET("/favicon.ico", func(c *gin.Context) {
// c.String(200, "值:%v", "首页图标")
// })
// r.GET("/ping", func(c *gin.Context) {
// c.JSON(200, gin.H{
// "message": "pong",
// })
// })
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
JSONP 主要用于跨域请求
func main() {
r := gin.Default()
r.GET("/JSONP",func(c *gin.Context) {
data := map[string]interface(){
"foo": "bar",
}
// /JSONP?callback=x
// 将输出:x({\"foo\":\"bar\"})
c.JSONP(http.StatusOK, data)
})
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":8080")
}
返回 xml 数据
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 = "IT 营学院"
msg.Message = "Hello world!"
msg.Age = 18
c.XML(http.StatusOK,msg)
})
}
简单的路由配置实践:
package main
import "net/http"
import "github.com/gin-gonic/gin"
// 定义一个结构体
type Article struct {
Title string `json:"title"`
Description string `json:"description"`
Content string `json:"content"`
}
// type Article struct {
// ID int `json:"id"`
// Title string `json:"title"`
// Content string `json:"content"`
// }
// type Login struct {
// Username string `json:"username"`
// Password string `json:"password"`
// }
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(200, "值:%v", "首页")
})
// 返回json数据
r.GET("/json1", func(c *gin.Context) {
c.JSON(200, map[string]interface{}{
"success": true,
"msg": "您好gin",
})
})
// 另一种写法 gin.H 就是 map[string]interface{}
r.GET("/json2", func(c *gin.Context) {
c.JSON(200, gin.H{
"success": true,
"msg": "您好gin,golang",
})
})
// 响应JSONP请求 可以传入回调函数 主要用于跨域请求
//http://localhost:8080/jsonp?callback=xxxx
//xxxx({"title":"gin入门---jsonp","description":"gin入门教程","content":"gin入门教程内容"});
r.GET("/jsonp", func(c *gin.Context) {
a := &Article{
Title: "gin入门---jsonp",
Description: "gin入门教程",
Content: "gin入门教程内容",
}
c.JSONP(200, a)
})
// r.GET("/xml", func(c *gin.Context) {
// c.XML(http.StatusOK, gin.H{
// "success": true,
// "msg": "您好gin,我是xml格式的返回数据",
// })
// })
// r.GET("/favicon.ico", func(c *gin.Context) {
// c.String(200, "值:%v", "首页图标")
// })
// r.GET("/ping", func(c *gin.Context) {
// c.JSON(200, gin.H{
// "message": "pong",
// })
// })
// 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 = "IT 营学院"
msg.Message = "Hello world!"
msg.Age = 18
c.XML(http.StatusOK, msg)
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
注意:gin.H就是 map[string]interface{}的缩写
渲染模板:
router.GET("/",func(c *gin.Context) {
c.HTML(http.StatusOK, "default/index.html",map[string]interface{}{
"title": "前台首页"
})
})
五,Gin HTML模板渲染
5.1,全部模板放在一个目录里面的配置方法
1,我们首先在项目根目录新建一个 templates 文件夹,然后在文件夹中新建 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>这是一个html模板</h1>
<h3>{
{.title}}</h3>
</body>
</html>
Main website
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")
//router.LoadHTMLFiles("templates/template1.html","templates/template2.html")
router.GET("/",func(c *gin.Context) {
c.HTML(http.StatusOK,"index.html",gin.H{
"title": "Main website",
})
})
router.Run(":8080")
}
type Article struct {
Title string `json:"title"`
Desc string `json:"desc"`
Content string `json:"content"`
}
func main() {
r := gin.Default()
r.GET("/",func(c *gin.Context) {
c.String(200,"值:%v","首页")
})
r.GET("/json1",func(c *gin.Context) {
c.JSON(200,map[string]interface{} {
"success": true,
"msg": "您好gin",
})
})
}
5.2,模板放在不同目录里面的配置方法
Gin 框架中如果不同目录下面有同名模板的话我们需要使用下面方法加载模板
注意:定义模板的时候需要通过 define定义名称
templates/admin/index.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "admin/index.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" context="width=device-width,initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>后台模板</h1>
<h3> {
{.title}} </h3>
</body>
</html>
{
{end}}
templates/default/index.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "admin/index.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>后台模板</h1>
<h3> {
{.title}} </h3>
</body>
</html>
{
{ end }}
对模板进行分组的实践:
文件名 main.go 代码如下:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
// 定义结构体
type Article struct {
Title string `json:"title"`
Content string `json:"content"`
Author string `json:"author"`
}
func main() {
r := gin.Default()
// 配置模板目录 表示加载模板
r.LoadHTMLGlob("templates/**/*")
// 配置路由
// 前台首页路由配置
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "default/index.html", gin.H{
"title": "首页",
})
})
// 前台新闻页面路由配置
r.GET("/news", func(c *gin.Context) {
// 实例化结构体
article := Article{
Title: "新闻标题--Gin框架",
Content: "新闻内容--这是一个基于Go语言的Web框架。",
Author: "Gin",
}
c.HTML(http.StatusOK, "default/news.html", gin.H{
"title": "新闻页面",
"article": article,
})
})
// 后台首页路由配置
r.GET("/admin", func(c *gin.Context) {
c.HTML(http.StatusOK, "admin/index.html", gin.H{
"title": "后台首页",
})
})
// 后台台新闻页面路由配置
r.GET("/admin/news", func(c *gin.Context) {
// 实例化结构体
article := Article{
Title: "后台新闻标题--Gin框架",
Content: "后台新闻内容--这是一个基于Go语言的Web框架。",
Author: "后台Gin",
}
c.HTML(http.StatusOK, "admin/news.html", gin.H{
"title": "后台新闻页面",
"article": article,
})
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
admin模板下的代码如下:
1, 文件 admin/index.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "admin/index.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>这是后台首页</h1>
</body>
</html>
{
{ end }}
2, 文件 admin/news.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "admin/news.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>这是后台新闻页面</h1>
</body>
</html>
{
{ end }}
default 模板下的代码如下:
1,文件 default/index.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "default/index.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>首页标题</title>
</head>
<body>
<h1> {
{.title}} </h1>
</body>
</html>
{
{ end }}
2, 文件 default/news.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "default/news.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新闻页面</title>
</head>
<body>
<h1> {
{.title}} </h1>
<p> {
{.article.Title}} </p>
<p> {
{.article.Content}} </p>
<p> {
{.article.Author}} </p>
</body>
</html>
{
{ end }}
5.3,gin模板基本语法
1,{ {.}} 输出数据
模板语法都包含在{ {和}}中间,其中{ {.}}中的点表示当前对象。
当我们传入一个结构体对象时,我们可以根据 . 来访问结构体的对应字段。例如:
业务逻辑
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type UserInfo struct {
Name string
Gender string
Age int
}
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/**/*")
user := UserInfo{
Name: "张三",
Gender: "男",
Age: 20,
}
router.GET("/",func(c *gin.Context) {
c.HTML(http.StatusOK,"default/index.html",map[string]interface{}{
"title": "前台首页",
"user": user,
})
})
router.Run(":8080")
}
2,变量
我们还可以在模板中声明变量,用来保存传入模板的数据或其他语句生成的结果。具体语法如下:
<h4> {
{$obj := .title}} </h4>
<h4> {
{$obj}} </h4>
3, 移除空格
有时候我们在使用模板语法的时候会不可避免的引入一些空格或者换行符,这样模板最终渲染出来的内容可能就和我们想的不一样,这个时候可以使用{ {-语法去除模板内容左侧的所有空白符号,使用-}}去除模板内容右侧的所有空白符号。
例如:
{
{- .Name -}}
注意:-要紧挨{ {和}},同时与模板之间需要使用空白分隔。
4,比较函数
布尔函数会将任何类型的零值视为假,其余为真。
下面是定义为函数的二元比较运算的集合:
eq 判断两个值是否相等 | 如果 arg1==arg2则返回真 |
---|---|
ne 判断两个值是否不相等 | 如果arg1 != arg2 则返回真 |
lt 判断左侧的值是否小于右侧的值 | 如果arg1<arg2则返回真 |
le 判断左侧的值是否小于或等于右侧的值 | 如果arg1<= arg2 则返回真 |
gt 判断左侧的值是否大于右侧的值 | 如果arg1>arg2 则返回真 |
ge 判断左侧的值是否大于或等于右侧的值 | 如果arg1 >= arg2 则返回真 |
5,条件判断
Go 模板语法中的条件判断有以下几种:
{
{if pipeline}} T1 {
{end}}
{
{if pipeline}} T1 {
{else}} TO {
{end}}
{
{if pipeline}} T1 {
{else if pipeline}} TO {
{end}}
{
{if gt .score 60}}
及格
{
{else}}
不及格
{
{end}}
{
{if gt .score 90}}
优秀
{
{else if gt .score 60}}
及格
{
{else}}
不及格
{
{end}}
6, range
Go 的模板语法中使用 range 关键字进行遍历,有以下两种写法,其中 pipeline的值必须是数组,切片,字典或者通道。
{
{range $key,$value := .obj}}
{
{$value}}
{
{end}}
如果 pipeline 的值其长度为0,不会有任何输出
{
{range $key,$value := obj}}
{
{$value}}
{
{else}}
pipeline的值其长度为0
{
{end}}
如果 pipeline 的值长度为0,则会执行T0.
router.GET("/",func(c *gin.Context) {
c.HTML(http.StatusOK,"default/index.html",map[string]interface{}{
"hobby":[]string{"吃饭","睡觉","写代码"},
})
})
{
{range $key,$value := .hobby}}
<p> {
{$value}} </p>
{
{end}}
7,With
user := UserInfo{
Name: "张三",
Gender: "男",
Age: 18,
}
router.GET("/",func(c *gin.Context) {
c.HTML(http.StatusOK,"default/index.html",map[string]interface{}{
"user": user,
})
})
以前要输出数据:
<h4>{
{.user.Name}}</h4>
<h4>{
{.user.Gender}}</h4>
<h4>{
{.user.Age}}</h4>
现在要输出数据:
{
{with .user}}
<h4>姓名: {
{.Name}}</h4>
<h4>性别: {
{.Gender}}</h4>
<h4>年龄: {
{.Age}}</h4>
{
{end}}
简单理解:相当于 var .=.user
8,预定义函数 (了解)
执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一
般不在模板内定义函数,而是使用 Func 方法添加函数到模板里。
预定义的全局函数如下:
and
函数返回它的第一个empty参数或者最后一个参数;
就是说"and x y"等价于 "if x else y": 所有参数都会执行;
or
返回第一个非 empty 参数或者最后一个参数;
亦即"or x y" 等价与 "if x then x else y" : 所有参数都会执行;
not
返回它的单个参数的布尔值的否定
len
返回它的参数的整数类型长度
index
执行结果为第一个参数以剩下的参数为索引、键指向的值;
如"index x 1 2 3"返回x12的值: 每个被索引的主体必须是数组,切片或者字典。
即 fmt.Sprint
printf
即 fmt.Sprintf
println
即 fmt.Sprintln
html
返回与其参数的文本表示形式等效的转义HTML.
这个函数在html/template中不可用。
urlquery
以适合嵌入到网址查询中的形式返回其参数的文本表示的转义值。
这个函数在 html/template中不可用。
js
返回与其参数的文本表示形式等效的转义 JavaScript.
call
执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数:
如"call .x.y 1 2"等价与go语言里的dot.X.Y(1,2);
其中Y是函数类型的字段或者字典的值,或者其他类似情况:
call的第一个参数执行结果必须是函数类型的值(和预定义函数如 print明显不同)
该函数类型值必须要1到2个 返回值。如果用2个则后一个必须是error接口类型:
如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误:
{ {len .title}} { {index .hobby 2}}
如何配置分组模板和路由配置实践:
1,文件 main.go
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Article struct {
Title string
Content string
}
func main() {
r := gin.Default()
// 加载模板 放在配置路由上面
r.LoadHTMLGlob("templates/**/*")
// 前台 首页路由配置
r.GET("/",func(c *gin.Context) {
c.HTML(http.StatusOK,"default/index.html",gin.H{
"title": "首页",
"msg": "我是msg",
"score": 89,
"hobby": []string{"吃饭","睡觉","写代码"},
"newsList": []interface{}{
&Article{
Title: "新闻标题111",
Content: "新闻详情111",
},
&Article{
Title: "新闻标题222",
Content: "新闻详情222",
},
},
"testSlice": []string{},
"news": &Article{
Title: "新闻标题",
Content: "新闻内容",
},
})
})
// 前台新闻页面路由配置
r.GET("/news",func(c *gin.Context) {
// 实例化结构体
article := Article{
Title: "新闻标题",
Content: "新闻内容",
}
c.HTML(http.StatusOK,"default/news.html",gin.H{
"title": "新闻页面",
"article": article,
})
})
// 后台首页路由配置
r.GET("/admin",func(c *gin.Context) {
c.HTML(http.StatusOK,"admin/index.html",gin.H{
"title": "后台首页",
})
})
// 后台新闻页面路由配置
r.GET("/admin/news",func(c *gin.Context) {
// 实例化结构体
article := Article{
Title: "后台新闻标题",
Content: "后台新闻内容",
}
c.HTML(http.StatusOK,"admin/news.html",gin.H{
"title": "后台新闻页面",
"article": article,
})
})
r.Run()
}
2, 前台模板文件夹 default 下配置如下文件示例
(1) default/index.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "default/index.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>首页标题</title>
</head>
<body>
<h1> {
{.title}} </h1>
<!-- 定义变量,并使用变量: -->
{
{$t := .title}}
<br>
<h4>
{
{$t}}
</h4>
<!-- 条件判断: -->
{
{if ge .score 60}}
<p>及格</p>
{
{else}}
<p>不及格</p>
{
{end}}
{
{if gt .score 90}}
<p>优秀</p>
{
{else if gt .score 80}}
<p>良好</p>
{
{else if gt .score 60}}
<p>及格</p>
{
{else}}
<p>不及格</p>
{
{end}}
<!-- 循环遍历数据: -->
<ul>
{
{range $key,$value := .hobby}}
<li>{
{$key}}----{
{$value}}</li>
{
{end}}
</ul>
<br>
<ul>
{
{range $key,$value := .newsList}}
<li>{
{$key}}---{
{$value.Title}}---{
{$value.Content}}--{
{$value.Author}}</li>
{
{end}}
</ul>
<br>
<ul>
{
{range $key,$value := .testSlice}}
<li>{
{$key}}----{
{$value}}</li>
{
{else}}
<li>数组中没有数据</li>
{
{end}}
</ul>
<br>
<!-- with 解构结构体 -->
<p>{
{.news.Title}}</p>
<p>{
{.news.Content}}</p>
<p>{
{.news.Author}}</p>
<br>
<!-- {
{with .news}}
{
{.Title}}
{
{.Content}}
{
{.Author}}
{
{end}} -->
{
{with .news}}
<p>{
{.Title}}</p>
<p>{
{.Content}}</p>
<p>{
{.Author}}</p>
{
{end}}
<!-- 管道符: -->
</body>
</html>
{
{ end }}
(2) 文件 default/news.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "default/news.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新闻页面</title>
</head>
<body>
<h1> {
{.title}} </h1>
<p> {
{.article.Title}} </p>
<p> {
{.article.Content}} </p>
<p> {
{.article.Author}} </p>
</body>
</html>
{
{ end }}
3, 前台模板文件夹 admin 下配置如下文件示例
(1) admin/index.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "admin/index.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>这是后台首页</h1>
</body>
</html>
{
{ end }}
(2) admin/news.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "admin/news.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>这是后台新闻页面</h1>
</body>
</html>
{
{ end }}
9,自定义模板函数
router.SetFuncMap(template.FuncMap{
"formatDate": formatAsDate,
})
实例:
5.4,嵌套 template
1, 新建 templates/default/page_header.html
{
{ define "default/page_header.html" }}
<h1>这是一个头部</h1>
{
{end}}
2,外部引入
注意:
1)引入的名字为 page_header.html中定义的名字
2)引入的时候注意最后的点(.)
{
{template "public/page_header.html" .}}
如何加载引入一个公共模板 示例代码如下:
1, 编写公共头部文件 "public/page_header.html" 代码如下:
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "public/page_header.html" }}
<style>
h1{
background: #000;
color: #fff;
text-align: center;
}
</style>
<h1>
我是一个公共标题---{
{.title}}
</h1>
{
{end}}
2, 编写公共底部文件 "public/page_footer.html"
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "public/page_footer.html" }}
<style>
h1{
background: #000;
color: #fff;
text-align: center;
}
</style>
<h1>
我是一个公共底部---{
{.title}}
</h1>
{
{end}}
3, 如何引入公共头部文件与公共底部文件 具体示例代码如下:
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "default/news.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新闻页面</title>
</head>
<body>
{
{template "public/page_header.html" .}}
<h1> {
{.title}} </h1>
<p> {
{.article.Title}} </p>
<p> {
{.article.Content}} </p>
<p> {
{.article.Author}} </p>
<br>
<br>
{
{template "public/page_footer.html" .}}
</body>
</html>
{
{ end }}
六,静态文件服务
当我们渲染的 HTML 文件中引用了静态文件时,我们需要配置静态 web 服务
r.Static("/static","./static") 前面的 /static 表示路由 后面的 ./static 表示路径
func main() {
r := gin.Default()
r.Static("/static","./static")
r.LoadHTMLGlob("templates/**/*")
}
七,路由详解
路由(Routing)是由一个 URI(或者叫路径) 和一个特定的HTTP方法(GET,POST等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。
前面章节我们给大家介绍了路由基础以及路由配置,这里我们详细给大家讲讲路由传值,路由返回值。
7.1 GET POST以及获取 GET POST传值
7.1.1,GET请求传值
GET /user?uid=20&page=1
router.GET("/user",func(c *gin.Context) {
uid := c.Query("uid")
page := c.DefaultQuery("page","0")
c.String(200,"uid=%v page=%v",uid,page)
})
如何获取Get传值:http://localhost:8080/?username=zhangsan&age=20
文件 main.go
package main
import (
"fmt"
"html/template"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// 时间戳转换日期
func UnixToTime(timestamp int64) string {
fmt.Println(timestamp)
t := time.Unix(int64(timestamp), 0)
return t.Format("2006-01-02 15:04:05")
}
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 自定义模板函数 注意要把这个函数放在加载模板前面
r.SetFuncMap(template.FuncMap{
"UnixToTime": UnixToTime,
})
// 加载模板 放在配置路由前面
r.LoadHTMLGlob("templates/**/*")
// 配置 web 目录 第一个参数表示路由,第二个参数表示映射的目录
r.Static("/static", "./static")
// 前台 Get请求传值
r.GET("/", func(c *gin.Context) {
username := c.Query("username")
age := c.Query("age")
page := c.DefaultQuery("page", "1")
c.JSON(http.StatusOK, gin.H{
"username": username,
"age": age,
"page": page,
})
})
// 后台
r.GET("/admin", func(c *gin.Context) {
c.HTML(http.StatusOK, "admin/index.html", gin.H{
"title": "后台首页",
"time": 1537269665,
})
})
r.Run()
}
http://localhost:8080/?username=zhangsan&age=20
输出:
{"age":"20","page":"1","username":"zhangsan"}
如何获取Get传值: http://localhost:8080/article?id=18
文件 main.go
package main
import (
"fmt"
"html/template"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// 定义一个结构体,用于存放模板数据
type Article struct {
Title string `json:"title"`
Content string `json:"content"`
}
// 时间戳转换日期
func UnixToTime(timestamp int64) string {
fmt.Println(timestamp)
t := time.Unix(int64(timestamp), 0)
return t.Format("2006-01-02 15:04:05")
}
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 自定义模板函数 注意要把这个函数放在加载模板前面
r.SetFuncMap(template.FuncMap{
"UnixToTime": UnixToTime,
})
// 加载模板 放在配置路由前面
r.LoadHTMLGlob("templates/**/*")
// 配置 web 目录 第一个参数表示路由,第二个参数表示映射的目录
r.Static("/static", "./static")
// 前台 Get请求传值
r.GET("/", func(c *gin.Context) {
username := c.Query("username")
age := c.Query("age")
page := c.DefaultQuery("page", "1")
c.JSON(http.StatusOK, gin.H{
"username": username,
"age": age,
"page": page,
})
})
// 前台 Get请求传值
r.GET("/article", func(c *gin.Context) {
id := c.DefaultQuery("id", "1")
c.JSON(http.StatusOK, gin.H{
"msg": "新闻详情",
"id": id,
})
})
// 后台
r.GET("/admin", func(c *gin.Context) {
c.HTML(http.StatusOK, "admin/index.html", gin.H{
"title": "后台首页",
"time": 1537269665,
})
})
r.Run()
}
http://localhost:8080/article?id=18
输出:
{"id":"18","msg":"新闻详情"}
7.1.2 动态路由传值
域名/user/20
r.GET("/user/:uid",func(c *gin.Context) {
uid := c.Param("uid")
c.String(http.StatusOK,"userID=%s",uid)
})
文件名: main.go
package main
import (
"github.com/gin-gonic/gin"
)
// 时间戳转换日期
func UnixToTime(timestamp int) string {
fmt.Println(timestamp)
t := time.Uinx(int64(timestamp),0)
return t.Format("2006-01-02 15:04:05")
}
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 自定义模板函数 注意要把这个函数放在加载模板前面
r.SetFuncMap(template.FuncMap{
"UnixToTime": UnixToTime,
})
// 加载模板 放在配置路由前面
r.LoadHTMLGlob("templates/**/*")
// 配置 web 目录 第一个参数表示路由,第二个参数表示映射的目录
r.Static("/static","./static")
// 前台
r.GET("/",func(c *gin.Content) {
c.String(http.StatusOK,"首页")
})
// 后台
r.GET("/admin",func(c *gin.Context) {
c.HTML(http.StatusOK, "admin/index.html",gin.H{
"title": "后台首页",
})
})
r.Run()
}
如何获取get传值?
7.1.3 Post请求传值,获取form表单数据
1) 定义一个 add_user.html的页面
{
{define "default/add_user.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Document</title>
</head>
<body>
<from action="/doAddUser" method="post"><br/><br/>
用户名: <input type="text" name="username" /><br/><br/>
密码: <input type="password" name="password" /><br/><br/>
<input type="submit" value="提交">
</body>
</html>
{
{end}}
2) 通过 c.PostForm 接收表单传过来的数据
router.GET("addUser",func(c *gin.Context) {
c.HTML(http.StatusOK,"default/add_user.html",gin.H{})
})
router.POST("/doAddUser", func(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
age := c.DefaultPostForm("age","20")
c.JSON(httpStatusOK,gin.H{
"username": username,
"password": password,
"age": age,
})
})
Post请求传值,获取form表单数据实践:
1 前端文件 user.html
<!--相当于给模板定义一个名字 define end 成对出现-->
{
{ define "default/user.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新闻页面</title>
<link rel="stylesheet" href="/static/css/base.css">
</head>
<body>
<form action="doAddUser" method="post">
用户名: <input type="text" name="username"/><br/><br/>
密码: <input type="password" name="password"/><br/><br/>
邮箱: <input type="text" name="email"/><br/><br/>
性别: <input type="radio" name="sex" value="男"/>男
<input type="radio" name="sex" value="女"/>女 <br/><br/>
年龄: <input type="text" name="age"/><br/><br/>
<input type="submit" value="提交"/><br/><br/>
</form>
</body>
</html>
{
{ end }}
2 后端文件 main.go
package main
import (
"fmt"
"html/template"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// 定义一个结构体,用于存放模板数据
type Article struct {
Title string `json:"title"`
Content string `json:"content"`
}
// 时间戳转换日期
func UnixToTime(timestamp int64) string {
fmt.Println(timestamp)
t := time.Unix(int64(timestamp), 0)
return t.Format("2006-01-02 15:04:05")
}
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 自定义模板函数 注意要把这个函数放在加载模板前面
r.SetFuncMap(template.FuncMap{
"UnixToTime": UnixToTime,
})
// 加载模板 放在配置路由前面
r.LoadHTMLGlob("templates/**/*")
// 配置 web 目录 第一个参数表示路由,第二个参数表示映射的目录
r.Static("/static", "./static")
// 前台 Get请求传值
r.GET("/", func(c *gin.Context) {
username := c.Query("username")
age := c.Query("age")
page := c.DefaultQuery("page", "1")
c.JSON(http.StatusOK, gin.H{
"username": username,
"age": age,
"page": page,
})
})
// 前台 Get请求传值
r.GET("/article", func(c *gin.Context) {
id := c.DefaultQuery("id", "1")
c.JSON(http.StatusOK, gin.H{
"msg": "新闻详情",
"id": id,
})
})
//POST演示
r.GET("/user", func(c *gin.Context) {
c.HTML(http.StatusOK, "default/user.html", gin.H{})
})
// 获取表单post过来的数据
r.POST("/doAddUser", func(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
email := c.PostForm("email")
sex := c.DefaultPostForm("sex", "1")
age := c.DefaultPostForm("age", "18")
c.JSON(http.StatusOK, gin.H{
"username": username,
"password": password,
"email": email,
"sex": sex,
"age": age,
})
})
// 前台 Post请求传值
// r.POST("/login", func(c *gin.Context) {
// username := c.PostForm("username")
// password := c.PostForm("password")
// c.JSON(http.StatusOK, gin.H{
// "msg": "登录成功",
// "data": Article{
// Title: username + "的文章标题",
// Content: password + "的文章内容",
// },
// })
// })
// 后台
r.GET("/admin", func(c *gin.Context) {
c.HTML(http.StatusOK, "admin/index.html", gin.H{
"title": "后台首页",
"time": 1537269665,
})
})
r.Run()
}
7.1.4, 获取 GET POST 传递的数据绑定到结构体
为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的 Content-Type 识别请求数据类型并利用反射机制自动提取请求中 QueryString,form表单,JSON,XML等参数到结构体。下面的示例代码演示了 .ShouldBind()强大的功能,它能够基于请求自动提取JSON,form表单和QueryString类型的数据,并把值绑定到指定的结构体对象。
// 定义结构体
// 注意首字母大写
type Userinfo struct {
Username string `form:"username" json:"user"`
Password string `form:"password" json:"password"`
}
Get 传值绑定到结构体
/?username=zhangsan&password=123456
router.GET("/",func(c *gin.Context) {
var userinfo Userinfo
if err := c.ShouldBind(&userinfo); err == nil {
c.JSON(http.StatusOK,userinfo)
} else {
c.JSON(http.StatusBadRequest,gin.H{"error": err.Error()})
}
})
返回数据
{"user":"zhangsan","password":"123456"}
Post传值绑定到结构体:
1,文件 main.go 后台修改值
router.POST("/doLogin",func(c *gin.Context) {
var userinfo Userinfo
if err := c.ShouldBind(&userinfo);err == nil {
c.JSON(http.StatusOK,userinfo)
} else {
c.JSON(http.StatusBadRequest,gin.H{"error": err.Error()})
}
})
2,文件 user.html 前端修改值
<body>
<form action="doLogin" method="post">
用户名: <input type="text" name="username"/><br/><br/>
密码: <input type="password" name="password"/><br/><br/>
邮箱: <input type="text" name="email"/><br/><br/>
性别: <input type="radio" name="sex" value="男"/>男
<input type="radio" name="sex" value="女"/>女 <br/><br/>
年龄: <input type="text" name="age"/><br/><br/>
<input type="submit" value="提交"/><br/><br/>
</form>
</body>
Get 传值绑定到结构体实践:
文件: main.go
package main
import (
"fmt"
"html/template"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// 定义一个结构体,用于存放模板数据
type Article struct {
Title string `json:"title"`
Content string `json:"content"`
}
// 定义一个结构体
type UserInfo struct {
Username string `json:"username" form:"username" `
Password string `json:"password" form:"password" `
// Email string `json:"email" form:"email" `
// Sex int `json:"sex" form:"sex" "`
// Age int `json:"age" form:"age" `
}
// 时间戳转换日期
func UnixToTime(timestamp int64) string {
fmt.Println(timestamp)
t := time.Unix(int64(timestamp), 0)
return t.Format("2006-01-02 15:04:05")
}
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// 自定义模板函数 注意要把这个函数放在加载模板前面
r.SetFuncMap(template.FuncMap{
"UnixToTime": UnixToTime,
})
// 加载模板 放在配置路由前面
r.LoadHTMLGlob("templates/**/*")
// 配置 web 目录 第一个参数表示路由,第二个参数表示映射的目录
r.Static("/static", "./static")
// 前台 Get请求传值
r.GET("/", func(c *gin.Context) {
username := c.Query("username")
age := c.Query("age")
page := c.DefaultQuery("page", "1")
c.JSON(http.StatusOK, gin.H{
"username": username,
"age": age,
"page": page,
})
})
// 前台 Get请求传值
r.GET("/article", func(c *gin.Context) {
id := c.DefaultQuery("id", "1")
c.JSON(http.StatusOK, gin.H{
"msg": "新闻详情",
"id": id,
})
})
//POST演示
r.GET("/user", func(c *gin.Context) {
c.HTML(http.StatusOK, "default/user.html", gin.H{})
})
// 获取表单post过来的数据
r.POST("/doAddUser", func(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
email := c.PostForm("email")
sex := c.DefaultPostForm("sex", "1")
age := c.DefaultPostForm("age", "18")
c.JSON(http.StatusOK, gin.H{
"username": username,
"password": password,
"email": email,
"sex": sex,
"age": age,
})
})
// 获取 GET POST 传递的数据绑定到结构体中
r.GET("/getUser", func(c *gin.Context) {
// 实例化结构体
user := &UserInfo{}
// 绑定前台传递过来的数据到结构体中
if err := c.ShouldBind(&user); err == nil {
fmt.Printf("%#v",user)
c.JSON(http.StatusOK, user)
return
} else {
c.JSON(http.StatusOK, gin.H{
"error": err.Error(),
})
}
})
// 后台
r.GET("/admin", func(c *gin.Context) {
c.HTML(http.StatusOK, "admin/index.html", gin.H{
"title": "后台首页",
"time": 1537269665,
})
})
r.Run()
}
1, 如何将get传递的数据绑定到结构体中实践:
// 获取 GET POST 传递的数据绑定到结构体中
// http://localhost:8080/getUser?username=zhangsan&password=11111&email=zhangsan@163.com
// 1, 如何将get传递的数据绑定到结构体中
r.GET("/getUser", func(c *gin.Context) {
// 实例化结构体
user := &UserInfo{}
// 绑定前台传递过来的数据到结构体中
if err := c.Shou