二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • Shell编程

  • Go编程笔记

  • 前端编程笔记

  • Go学习笔记

    • 基础部分

    • web框架

      • 标准库template学习入门
      • gin框架学习入门
        • 1,Gin 框架介绍
        • 2,Gin 框架安装与使用
          • 1,安装
          • 2,第一个 Gin 示例:
        • 3,RESTful API
        • 4,gin 渲染
          • 1,返回 json
        • 5,获取参数
          • 1,获取 querystring 参数
          • 2,获取 form 表单
          • 3,获取 path 参数
          • 4,参数绑定
        • 6,路由重定向
        • 7,路由组
        • 8,中间件
          • 1,定义中间件
          • 2,注册中间件
          • 1,为某个路由单独注册
          • 2,为全局路由注册
          • 3,为路由组注册中间件
          • 3,中间件执行顺序
          • 1,c.Next()
          • 2,c.Abort()
          • 3,return
          • 4,c.Set()和 c.Get()
          • 4,登陆中间件实例
          • 3,中间件注意事项
          • 2,gin 默认中间件
          • 3,gin 中间件中使用 goroutine
      • 解决前端调用后端跨域问题
      • 通过jwt基于token实现登陆认证
    • orm框架

  • Vue-21年学习笔记

  • Vue-22年重学笔记

  • 编程世界
  • Go学习笔记
  • web框架
二丫讲梵
2021-07-10
目录

gin框架学习入门

文章发布较早,内容可能过时,阅读注意甄别。

Gin是一个用 Go 语言编写的 web 框架。它是一个类似于martini但拥有更好性能的 API 框架, 由于使用了httprouter,速度提高了近 40 倍。 如果你是性能和高效的追求者, 你会爱上Gin。

# 1,Gin 框架介绍

Go 世界里最流行的 Web 框架,Github (opens new window)上有32K+star。 基于httprouter (opens new window)开发的 Web 框架。 中文文档 (opens new window)齐全,简单易用的轻量级框架。

# 2,Gin 框架安装与使用

# 1,安装

下载并安装Gin:

go get -u github.com/gin-gonic/gin
1

# 2,第一个 Gin 示例:

package main

import (
	"github.com/gin-gonic/gin"
)

func main() {
	// 创建一个默认的路由引擎
	r := gin.Default()
	// GET:请求方式;/hello:请求的路径
	// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数
	r.GET("/hello", func(c *gin.Context) {
		// c.JSON:返回JSON格式的数据
		c.JSON(200, gin.H{
			"message": "Hello world!",
		})
	})
	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
	r.Run()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

将上面的代码保存并编译执行,然后使用浏览器打开127.0.0.1:8080/hello就能看到一串 JSON 字符串。

# 3,RESTful API

REST 与技术无关,代表的是一种软件架构风格,REST 是 Representational State Transfer 的简称,中文翻译为“表征状态转移”或“表现层状态转化”。

推荐阅读阮一峰 理解 RESTful 架构 (opens new window)

简单来说,REST 的含义就是客户端与 Web 服务器之间进行交互的时候,使用 HTTP 协议中的 4 个请求方法代表不同的动作。

  • GET用来获取资源
  • POST用来新建资源
  • PUT用来更新资源
  • DELETE用来删除资源。

只要 API 程序遵循了 REST 风格,那就可以称其为 RESTful API。目前在前后端分离的架构中,前后端基本都是通过 RESTful API 来进行交互。

例如,我们现在要编写一个管理书籍的系统,我们可以查询对一本书进行查询、创建、更新和删除等操作,我们在编写程序的时候就要设计客户端浏览器与我们 Web 服务端交互的方式和路径。按照经验我们通常会设计成如下模式:

请求方法 URL 含义
GET /book 查询书籍信息
POST /create_book 创建书籍记录
POST /update_book 更新书籍信息
POST /delete_book 删除书籍信息

同样的需求我们按照 RESTful API 设计如下:

请求方法 URL 含义
GET /book 查询书籍信息
POST /book 创建书籍记录
PUT /book 更新书籍信息
DELETE /book 删除书籍信息

Gin 框架支持开发 RESTful API 的开发。

func main() {
	r := gin.Default()
	r.GET("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "GET",
		})
	})

	r.POST("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "POST",
		})
	})

	r.PUT("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "PUT",
		})
	})

	r.DELETE("/book", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "DELETE",
		})
	})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

开发 RESTful API 的时候我们通常使用Postman (opens new window)来作为客户端的测试工具。

# 4,gin 渲染

# 1,返回 json

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	// 方法一:通过map返回
	//data := map[string]interface{}{
	//	"name": "eryajf",
	//	"age":  20,
	//}
	// gin提供了一个提前定义好了的map H,方便我们直接调用
	data := gin.H{"name": "eryajf", "age": 20} // type H map[string]interface{}
	r.GET("/json", func(c *gin.Context) {
		c.JSON(http.StatusOK, data)
	})

	// 方法二:通过结构体 注意,结构体当中字段名称必须大写,因为下边c.JSON需要对结构体序列化,如果小写,则读不到数据
	// 如果仍旧想返回小写的字段,可以通过tag来定义
	type msg struct {
		Name    string `json:"name"`
		Message string `json:"message"`
		Age     int    `json:"age"`
	}
	data1 := msg{
		Name:    "二丫讲梵",
		Message: "hello golang",
		Age:     22,
	}
	r.GET("/otherjson", func(c *gin.Context) {
		c.JSON(http.StatusOK, data1)
	})

	r.Run(":9090")

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# 5,获取参数

# 1,获取 querystring 参数

querystring指的是 URL 中?后面携带的参数,例如:/user/search?username=小王子&address=沙河。 获取请求的 querystring 参数的方法如下:

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	// GET请求URL ?后面的是query string参数
	// key=value格式,多个用 & 连接

	r.GET("web", func(c *gin.Context) {
		// 获取浏览器那边发请求携带的 query string 参数
		//方式一   http://127.0.0.1:9090/web?query=eryajf&age=20
		name := c.Query("query") // 通过 query获取请求中携带的query string
		age := c.Query("age")
		//方式二
		//name := c.DefaultQuery("query", "somequery") //取不到就用指定的默认值
		//方式三
		//name, ok := c.GetQuery("query") //取不到query就返回false
		//if !ok {
		//	name = "somequery"
		//}
		c.JSON(http.StatusOK, gin.H{
			"name": name,
			"age":  age,
		})
	})
	r.Run(":9090")
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

此时请求 http://127.0.0.1:9090/web?query=eryajf&age=20

# 2,获取 form 表单

请求的数据通过 form 表单来提交,例如向/user/search发送一个 POST 请求,获取请求数据的方式如下,此处通过两个 html 文件简单模拟一下登陆页面。

创建一个登陆页面:login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>

<form action="/login" method="post">
    <div>
        <label for="username">username:</label>
        <input type="text" name="username" id="username">
    </div>
    <div>
        <label for="password">password:</label>
        <input type="text" name="password" id="password">
    </div>
    <div>
    <input type="submit" value="登陆">
    </div>
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

创建一个 index.html 用于返回用户登陆成功之后的页面:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>index</title>
  </head>
  <body>
    <h1>hello {{ .Name }}</h1>
    <h1>hello {{ .Pass }}</h1>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11

主程序内容如下:

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()

	r.LoadHTMLFiles("./login.html", "./index.html")

	r.GET("/login", func(c *gin.Context) {
		c.HTML(http.StatusOK, "login.html", nil)
	})
	// /login post
	r.POST("login", func(c *gin.Context) {
		// 方法一
		//username := c.PostForm("username")
		//password := c.PostForm("password")
		// 方法二
		//username := c.DefaultPostForm("username", "somev")
		//password := c.DefaultPostForm("password", "***")
		// 方法三
		username, ok := c.GetPostForm("username")
		if !ok {
			username = "somev"
		}
		password, ok := c.GetPostForm("password")
		if !ok {
			password = "***"
		}
		c.HTML(http.StatusOK, "index.html", gin.H{
			"Name": username,
			"Pass": password,
		})
	})

	r.Run(":9090")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 3,获取 path 参数

请求的参数通过 URL 路径传递,例如:/user/search/小王子/沙河。 获取请求 URL 路径中的参数的方式如下。

package main

// 获取请求的path (URI) 返回的都是字符串类型
// 注意URL的匹配不要冲突了
import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.GET("/user/:name/:age", func(c *gin.Context) {	// eg: curl 127.0.0.1:9090/user/eryajf/20
		name := c.Param("name")
		age := c.Param("age") //string类型
		c.JSON(http.StatusOK, gin.H{
			"name": name,
			"age":  age,
		})
	})
	// 模拟查询博客某年某月的文章列表
	r.GET("blog/:year/:month", func(c *gin.Context) {	// eg: curl 127.0.0.1:9090/blog/2019/10
		year := c.Param("year")
		month := c.Param("month")
		c.JSON(http.StatusOK, gin.H{
			"year":  year,
			"month": month,
		})
	})

	r.Run(":9090")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 4,参数绑定

为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryString、form表单、JSON、XML等参数到结构体中。 下面的示例代码演示了.ShouldBind()强大的功能,它能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象。

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

type userInfo struct {
	Username string `form:"username" json:"user"`
	Password string `form:"password" json:"pwd"`
}

func main() {
	r := gin.Default()
	r.GET("/user", func(c *gin.Context) {
		username := c.Query("username")
		password := c.Query("password")
		u := userInfo{
			Username: username,
			Password: password,
		}
		c.JSON(http.StatusOK, gin.H{
			"user": u.Username,
			"pass": u.Password,
		})
	})
	// 利用 绑定参数方式可以方便的获取到浏览器请求参数 querystring
	r.GET("/bindquery", func(c *gin.Context) {
		var u userInfo
		if err := c.ShouldBind(&u); err != nil {
			c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
		} else {
			c.JSON(http.StatusOK, gin.H{
				"user": u.Username,
				"pass": u.Password,
			})
		}
	})
	// 利用 绑定参数方式可以方便的获取到浏览器表单参数 提交普通的参数
	r.POST("/bindform", func(c *gin.Context) {
		var u userInfo
		if err := c.ShouldBind(&u); err != nil {
			c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
		} else {
			c.JSON(http.StatusOK, gin.H{
				"user": u.Username,
				"pass": u.Password,
			})
		}
	})
	// 利用 绑定参数方式可以方便的获取到浏览器表单参数 提交json内容
	r.POST("/bindjson", func(c *gin.Context) {
		var u userInfo
		if err := c.ShouldBind(&u); err != nil {
			c.JSON(http.StatusBadGateway, gin.H{"error": err.Error()})
		} else {
			c.JSON(http.StatusOK, gin.H{
				"user": u.Username,
				"pass": u.Password,
			})
		}
	})

	r.Run(":9090")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

其中发起 post 请求的时候,上边两种方法有所不同,请求 /bindform,方法如下:

image-20200306173921901

请求 /bindjson,方法如下:

image-20200306174348341

# 6,路由重定向

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()

	r.GET("index", func(c *gin.Context) {
		//c.JSON(http.StatusOK, gin.H{
		//	"msg": "ok",
		//})
		// 将 /index 请求跳转到 其他域名
		c.Redirect(http.StatusMovedPermanently, "http://eryajf.net")
	})

	r.GET("/a", func(c *gin.Context) {
		// 将 /a 跳转到 /b
		c.Request.URL.Path = "/b" //把请求的URL修改
		r.HandleContext(c)        //继续后续的处理
	})
	r.GET("/b", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "b",
		})
	})

	r.Run(":9090")
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 7,路由组

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.GET("index", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"method": "GET",
		})
	})
	r.POST("index", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"method": "POST",
		})
	})
	r.PUT("index", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"method": "PUT",
		})
	})
	r.DELETE("index", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"method": "DELETE",
		})
	})

	// Any 可以接收任意方法
	r.Any("user", func(c *gin.Context) {
		switch c.Request.Method {
		case "GET":
			c.JSON(http.StatusOK, gin.H{"method": "GET"})
		case http.MethodPost:
			c.JSON(http.StatusOK, gin.H{"method": "POST"})
		}
	})

	// NoRoute 可以接收任意未定义的路由请求
	r.NoRoute(func(c *gin.Context) {
		c.JSON(http.StatusNotFound, gin.H{"msg": "来到了荒漠"})//也可以指向固定的404.html页面
	})

	// 定义一个视频首页以及详情
	//r.GET("vedio/indxe", func(c *gin.Context) {
	//	c.JSON(http.StatusOK, gin.H{"msg": "/vedio/index"})
	//})
	//r.GET("vedio/xx", func(c *gin.Context) {
	//	c.JSON(http.StatusOK, gin.H{"msg": "/vedio/xx"})
	//})
	//r.GET("vedio/oo", func(c *gin.Context) {
	//	c.JSON(http.StatusOK, gin.H{"msg": "/vedio/oo"})
	//})	可以用如下路由组方式定义
	vedioGroup := r.Group("vedio")
	{ //通常用一个大括号作为代码块包起来
		vedioGroup.GET("index", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"msg": "/vedio/index"})
		})
		vedioGroup.GET("xx", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"msg": "/vedio/xx"})
		})
		vedioGroup.GET("oo", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"msg": "/vedio/oo"})
		})
	}

	r.Run(":9090")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

路由嵌套:

shopGroup := r.Group("/shop")
	{
		shopGroup.GET("/index", func(c *gin.Context) {...})
		shopGroup.GET("/cart", func(c *gin.Context) {...})
		shopGroup.POST("/checkout", func(c *gin.Context) {...})
		// 嵌套路由组
		xx := shopGroup.Group("xx")
		xx.GET("/oo", func(c *gin.Context) {...})
	}
1
2
3
4
5
6
7
8
9

# 8,中间件

Gin 框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。

# 1,定义中间件

Gin 中的中间件必须是一个gin.HandlerFunc类型。例如我们像下面的代码一样定义一个统计请求耗时的中间件。

// 定义一个中间件用于统计函数处理请求的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	start := time.Now()
	c.Next() //调用后续的处理函数
	//c.Abort() //阻止调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}
1
2
3
4
5
6
7
8
9
10

# 2,注册中间件

在 gin 框架中,我们可以为每个路由添加任意数量的中间件。

# 1,为某个路由单独注册

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"time"
)

func indexHandler(c *gin.Context) {
	fmt.Println("index...")
	c.JSON(http.StatusOK, gin.H{
		"msg": "index...",
	})
}
func shopHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "shop...",
	})
}
func userHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "user...",
	})
}

// 定义一个中间件用于统计函数处理请求的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	start := time.Now()
	c.Next() //调用后续的处理函数
	//c.Abort() //阻止调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}

func main() {
	r := gin.Default()

	// GET(relativePath string, handlers ...HandlerFunc)
	r.GET("/index", m1, indexHandler)
	r.GET("/shop", m1, indexHandler)
	r.GET("/user", indexHandler)

	r.Run()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

同一个中间件可以注册给指定的路由,不过如果想要把这个中间件授权给所有路由组,就需要用如下方式了。

# 2,为全局路由注册

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"time"
)

func indexHandler(c *gin.Context) {
	fmt.Println("index...")
	c.JSON(http.StatusOK, gin.H{
		"msg": "index...",
	})
}
func shopHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "shop...",
	})
}
func userHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "user...",
	})
}

// 定义一个中间件用于统计函数处理请求的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	start := time.Now()
	c.Next() //调用后续的处理函数
	//c.Abort() //阻止调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}

func main() {
	r := gin.Default()

	r.Use(m1)                 //全局注册中间件函数 m1
	r.GET("/index", indexHandler) //这样保持了以下路由原样不变,也新增了中间件进来
	r.GET("/shop", shopHandler)
	r.GET("/user", userHandler)
	r.Run()
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 3,为路由组注册中间件

为路由组注册中间件有以下两种写法。

写法 1:

shopGroup := r.Group("/shop", m1)
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}
1
2
3
4
5

写法 2:

shopGroup := r.Group("/shop")
shopGroup.Use(m1)
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}
1
2
3
4
5
6

# 3,中间件执行顺序

# 1,c.Next()

c.Next():表示执行下一个函数

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"time"
)

func indexHandler(c *gin.Context) {
	fmt.Println("index...")
	c.JSON(http.StatusOK, gin.H{
		"msg": "index...",
	})
}
func shopHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "shop...",
	})
}
func userHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "user...",
	})
}

// 定义一个中间件用于统计函数处理请求的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	start := time.Now()
	c.Next() //调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}

func m2(c *gin.Context) {
	fmt.Println("m2 in...")
	c.Next()
	fmt.Println("m2 out...")
}

func main() {
	r := gin.Default()

	//r.Use(m1)                 //全局注册中间件函数 m1
	r.Use(m1, m2)                 //可以注册多个中间件,注意中间件执行的顺序
	r.GET("/index", indexHandler) //这样保持了以下路由原样不变,也新增了中间件进来
	r.GET("/shop", shopHandler)
	r.GET("/user", userHandler)

	r.Run()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

如果请求 index,将打印:

m1 in...
m2 in...
index...
m2 out...
cost:232.264µs
m1 out...
1
2
3
4
5
6

执行图示如下:

image-20200308101949467

具体函数流程,如下:

image-20200308102049034

# 2,c.Abort()

c.Abort:表示取消执行后边的函数

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"time"
)

func indexHandler(c *gin.Context) {
	fmt.Println("index...")
	c.JSON(http.StatusOK, gin.H{
		"msg": "index...",
	})
}
func shopHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "shop...",
	})
}
func userHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "user...",
	})
}

// 定义一个中间件用于统计函数处理请求的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	start := time.Now()
	c.Next() //调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}

func m2(c *gin.Context) {
	fmt.Println("m2 in...")
	//c.Next()
	c.Abort() //阻止调用后续的处理函数
	fmt.Println("m2 out...")
}

func main() {
	r := gin.Default()

	//r.Use(m1)                 //全局注册中间件函数 m1
	r.Use(m1, m2)                 //可以注册多个中间件,注意中间件执行的顺序
	r.GET("/index", indexHandler) //这样保持了以下路由原样不变,也新增了中间件进来
	r.GET("/shop", shopHandler)
	r.GET("/user", userHandler)

	r.Run()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

执行如上代码,将会看到如下输出:

m1 in...
m2 in...
m2 out...
cost:232.264µs
m1 out...
1
2
3
4
5

进入到 m2 in 之后不再执行响应请求的函数,而是直接来到 m2 out。图示如下:

image-20200308102512239

# 3,return

return:随时从某个地方跳出


package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"time"
)

func indexHandler(c *gin.Context) {
	fmt.Println("index...")
	c.JSON(http.StatusOK, gin.H{
		"msg": "index...",
	})
}
func shopHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "shop...",
	})
}
func userHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "user...",
	})
}

// 定义一个中间件用于统计函数处理请求的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	start := time.Now()
	c.Next() //调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}

func m2(c *gin.Context) {
	fmt.Println("m2 in...")
	//c.Next()
	c.Abort() //阻止调用后续的处理函数
	return
	fmt.Println("m2 out...")
}

func main() {
	r := gin.Default()

	//r.Use(m1)                 //全局注册中间件函数 m1
	r.Use(m1, m2)                 //可以注册多个中间件,注意中间件执行的顺序
	r.GET("/index", indexHandler) //这样保持了以下路由原样不变,也新增了中间件进来
	r.GET("/shop", shopHandler)
	r.GET("/user", userHandler)

	r.Run()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

如上代码执行结果如下:

m1 in...
m2 in...
cost:232.264µs
m1 out...
1
2
3
4

# 4,c.Set()和 c.Get()

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"time"
)

func indexHandler(c *gin.Context) {
	fmt.Println("index...")
	name, ok := c.Get("name")
	if !ok {
		name = "匿名用户"
	}
	c.JSON(http.StatusOK, gin.H{
		"msg": name,
	})
}

// 定义一个中间件用于统计函数处理请求的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	start := time.Now()
	c.Next() //调用后续的处理函数
	//c.Abort() //阻止调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}

func m2(c *gin.Context) {
	fmt.Println("m2 in...")
	//c.Next()
	c.Set("name", "eryajf")
	fmt.Println("m2 out...")
}

func main() {
	r := gin.Default()
	r.Use(m1, m2)                 //可以注册多个中间件,注意中间件执行的顺序
	r.GET("/index", indexHandler) //这样保持了以下路由原样不变,也新增了中间件进来
	r.Run()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

这里在 m2 这个中间件里边传递了一个 name 给后边的函数,然后 indexHandler 接收这个变量来使用。

# 4,登陆中间件实例

通常我们采用如下方式定义一个判断用户是否登陆的一个中间件:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"time"
)

func indexHandler(c *gin.Context) {
	fmt.Println("index...")
	c.JSON(http.StatusOK, gin.H{
		"msg": "index...",
	})
}
func shopHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "shop...",
	})
}
func userHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"msg": "user...",
	})
}

// 定义一个中间件用于统计函数处理请求的耗时
func m1(c *gin.Context) {
	fmt.Println("m1 in...")
	start := time.Now()
	c.Next() //调用后续的处理函数
	//c.Abort() //阻止调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out...")
}

func m2(c *gin.Context) {
	fmt.Println("m2 in...")
	c.Next()
	fmt.Println("m2 out...")
}

func authMiddleware(doCheck bool) gin.HandlerFunc {
	// 可以连接数据库
	// 或者一些其他的准备工作
	return func(c *gin.Context) {
		if doCheck {
			// 存放具体的逻辑
			// 判断是否登陆
			// if 是登陆用户
			// c.Next()
			// else
			// c.Abort()
		} else {
			c.Next()
		}
	}
}

func main() {
	r := gin.Default()

	r.Use(m1, m2)                 //可以注册多个中间件,注意中间件执行的顺序
	r.GET("/index", indexHandler) //这样保持了以下路由原样不变,也新增了中间件进来
	r.GET("/shop", authMiddleware(true), shopHandler)
	r.GET("/user", userHandler)
	r.Run()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

# 3,中间件注意事项

# 2,gin 默认中间件

gin.Default()默认使用了Logger和Recovery中间件,其中:

  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。
  • Recovery中间件会 recover 任何panic。如果有 panic 的话,会写入 500 响应码。

如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

# 3,gin 中间件中使用 goroutine

当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。

微信 支付宝
上次更新: 2024/07/04, 22:40:37
标准库template学习入门
解决前端调用后端跨域问题

← 标准库template学习入门 解决前端调用后端跨域问题→

最近更新
01
记录二五年五一之短暂回归家庭
05-09
02
学习周刊-总第210期-2025年第19周
05-09
03
学习周刊-总第209期-2025年第18周
05-03
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式