Gin框架 -- 中间件

1. 定义中间件

Gin中的中间件必须是一个gin.HandlerFunc类型,在自定义中间件函数时,有两种写法:

func AuthMiddleWare(c *gin.Context) { 
    
}
// 使用中间件
r.Use(AuthMiddleWare)

或者

func AuthMiddleWare() gin.HandlerFunc { 
    
}
// 使用中间件
r.Use(AuthMiddleWare())

2. 注册中间件

1. 使用gin.Engine分包后的注册中间件

main.go

package main

import (
	"fmt"
	"ginproject/app/auth"
	"ginproject/app/blog"
	"ginproject/app/shop"
	"ginproject/routers"
	"github.com/gin-gonic/gin"
)


func main() {
    // 导入shop和blog和auth下的路由文件
	routers.Include(shop.Routers, blog.Routers,auth.Routers)
	r := routers.Init()
	if err := r.Run("0.0.0.0:8009"); r != nil {
		panic(err.Error())
	}
}

routers/routers.go

package routers

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

type Option func(engine *gin.Engine)

var options = []Option{}

func Include(opts ...Option) {
	options = append(options, opts...)
}

func Init() *gin.Engine {
	r := gin.New()
	// 注册中间件
	r.Use(middlewares.MiddleWare())
	for _, opt := range options {
		opt(r)
	}
	return r
}

2. 全局中间件

func main() {
	// 新建一个没有任何默认中间件的路由
	r := gin.New()
	// r.Use注册全局中间件
	r.Use(AuthMiddleWare())  // 或者r.Use(AuthMiddleWare)
    
	r.GET("/test", func(c *gin.Context) {
		name := c.MustGet("name").(string) // 从上下文取值
		log.Println(name)
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello world!",
		})
	})
	r.Run()
}

3. 局部中间件

// 直接在路由中注册中间件(可注册多个)
	r.GET("/test2", AuthMiddleWare(), func(c *gin.Context) {
		name := c.MustGet("name").(string) // 从上下文取值
		log.Println(name)
		c.JSON(http.StatusOK, gin.H{
			"message": "Hello world!",
		})
	})

4. 路由组注册中间件

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

写法1:

// 中间件名为StatCostMiddleWare
shopGroup := r.Group("/shop", StatCostMiddleWare())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

写法2:

shopGroup := r.Group("/shop")
shopGroup.Use(StatCostMiddleWare())
{
    shopGroup.GET("/index", func(c *gin.Context) {...})
    ...
}

3. 中间件嵌套的调用方法

中间件可以嵌套使用,这里有三个相关的函数。

1. Next()

表示跳过当前中间件剩余内容, 去执行下一个中间件。 当所有操作执行完之后,以出栈的执行顺序返回,执行剩余代码。

package main

import (
    "fmt"
    "time"

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

// 定义中间
func MiddleWare() gin.HandlerFunc {
    return func(c *gin.Context) {
        t := time.Now()
        fmt.Println("中间件开始执行了")
        // 设置变量到Context的key中,可以通过Get()取
        c.Set("request", "中间件")
        // 执行函数
        c.Next()
        // 中间件执行完后续的一些事情
        status := c.Writer.Status()
        fmt.Println("中间件执行完毕", status)
        t2 := time.Since(t)
        fmt.Println("time:", t2)
    }
}

func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // 注册中间件
    r.Use(MiddleWare())
    // {}为了代码规范
    {
        r.GET("/ce", func(c *gin.Context) {
            // 取值
            req, _ := c.Get("request")
            fmt.Println("request:", req)
            // 页面接收
            c.JSON(200, gin.H{"request": req})
        })

    }
    r.Run()
}

2. return()

终止执行当前中间件剩余内容,执行下一个中间件。 当所有的函数执行结束后,以出栈的顺序执行返回,不执行return后的代码。但仍会执行handler函数

// 创建中间件
    func Test1(ctx *gin.Context)  {
    	fmt.Println("1111")
    	ctx.Next()
    	fmt.Println("4444")
    }
    // 创建 另外一种格式的中间件.
    func Test2() gin.HandlerFunc {
    	return func(context *gin.Context) {
    		fmt.Println("3333")
    		return
    		context.Next()
    		fmt.Println("5555")
    	}
    }
    func main()  {
    	router := gin.Default()

    	// 使用中间件
    	router.Use(Test1)
    	router.Use(Test2())

    	router.GET("/test", func(context *gin.Context) {
    		fmt.Println("2222")
    		context.Writer.WriteString("hello world!")
    	})

    	router.Run(":9999")
    }

3. Abort(),终止后续逻辑

//1. 为什么return阻止不了后续的逻辑运行?
//2. **c.Next()**如何开启下一个执行代码?
// 3. c.Abort()如何终止后续代码?
router.Use()会把相关的中间件函数名(CheckToken(), gin.Recovery(), gin.Logger())依次append到group.Handlers切片后边,后面的router.GET()也会把LoginFunc这个业务函数append到group.Handlers后面,先放入的会先开始运行。当我们在CheckToken()这个中间件中使用return时,它只会结束这个中间件的函数,不会影响到group.Handlers切片中函数的索引值Index,gin驱动会根据Index继续执行group.Handlers中的下一个函数(索引为Index+1)。

 中间件函数中的c.Next()方法会让待执行的函数开始执行的实现方式为:c.Next()会执行Index++,当Index值还在group.Handlers的长度内,gin驱动便调用group.Handlers中的索引为Index的待执行的函数。

 c.Abort()终止后续逻辑代码的实现方式为:对Index赋值为一个过大的数,使之超过group.Handlers的长度,使gin驱动无法调用到。

只执行当前中间件, 操作完成后,以出栈的顺序,依次返回上一级中间件。

// 创建中间件
    func Test1(ctx *gin.Context)  {
    	fmt.Println("1111")

    	ctx.Next()

    	fmt.Println("4444")
    }
    // 创建 另外一种格式的中间件.
    func Test2() gin.HandlerFunc {
    	return func(context *gin.Context) {
    		fmt.Println("3333")

    		context.Abort()

    		fmt.Println("5555")
    	}
    }
    func main()  {
    	router := gin.Default()

    	// 使用中间件
    	router.Use(Test1)
    	router.Use(Test2())

    	router.GET("/test", func(context *gin.Context) {
    		fmt.Println("2222")
    		context.Writer.WriteString("hello world!")
    	})

    	router.Run(":9999")
    }

4. 中间件注意事项

1. gin默认加载的中间件

// gin.Default()默认使用了Logger和Recovery中间件,其中:
// Logger
Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。
// Recovery
Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。

2. 创建无任何中间件的Gin框架

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

3. gin中间件中使用goroutine

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

5. 中间件练习

定义程序计时中间件,然后定义2个路由,执行函数后应该打印统计的执行时间,如下:

package main

import (
    "fmt"
    "time"

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

// 定义中间
func myTime(c *gin.Context) {
    start := time.Now()
    c.Next()
    // 统计时间
    since := time.Since(start)
    fmt.Println("程序用时:", since)
}

func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // 注册中间件
    r.Use(myTime)
    // {}为了代码规范
    shoppingGroup := r.Group("/shopping")
    {
        shoppingGroup.GET("/index", shopIndexHandler)
        shoppingGroup.GET("/home", shopHomeHandler)
    }
    r.Run(":8000")
}

func shopIndexHandler(c *gin.Context) {
    time.Sleep(5 * time.Second)
}

func shopHomeHandler(c *gin.Context) {
    time.Sleep(3 * time.Second)
}

效果

6. 不错的中间件推荐

1. gin.BasicAuth (认证中间件)

本示例是使用路由组来注册中间件

这个中间件应该提供了一整套的登录逻辑,

1. 首先会出现登录弹框,但是会被保存在前端的cookie中,
2. 输入内容,输入正确时,会返回响应数据,并表示此用户登录成功,将登录状态写入前端的cookie中,当用户再次访问时,则不需要登录,
3. 过期时间暂不清楚
4. 输入错误时,会一直弹框,直到输入正确

image-20230608144817501

官方例子

// 模拟一些私人数据
var secrets = gin.H{
	"foo":    gin.H{"email": "foo@bar.com", "phone": "123433"},
	"austin": gin.H{"email": "austin@example.com", "phone": "666"},
	"lena":   gin.H{"email": "lena@guapa.com", "phone": "523443"},
}

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

	// 路由组使用 gin.BasicAuth() 中间件
	// gin.Accounts 是 map[string]string数据结构的一种快捷实现方式
	authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
		"foo":    "bar",
		"austin": "1234",
		"lena":   "hello2",
		"manu":   "4321",
	}))

    // 请求URL: "http://127.0.0.1:8080/admin/secrets
	authorized.GET("/secrets", func(c *gin.Context) {
		// 获取用户,它是由 BasicAuth 中间件设置的
		user := c.MustGet(gin.AuthUserKey).(string)
		if secret, ok := secrets[user]; ok {
			c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
		} else {
			c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
		}
	})

	// 监听并在 0.0.0.0:8080 上启动服务
	r.Run(":8080")
}

自己实现的

// 预设校验信息,当然这个也可以是从数据库中查询
func initBasicAuth () gin.HandlerFunc {
	return gin.BasicAuth(gin.Accounts{
		"小明":"123",
		"小红":"123",
		"小白":"123",
	})
}

// 模拟一些私人数据
var secrets = gin.H{
	"小明": gin.H{"email": "foo@bar.com", "phone": "123433"},
	"小红": gin.H{"email": "austin@example.com", "phone": "666"},
	"小白": gin.H{"email": "lena@guapa.com", "phone": "523443"},
}

// 注册中间件
e.GET("/login",initBasicAuth(),loginHandler)

// handler函数
func loginHandler(ctx *gin.Context) {
    // 从请求中拿到请求的key
	user_name := ctx.MustGet(gin.AuthUserKey).(string)
    // 在内存中的secrets Map中查询是否有这个key
	if secret, ok := secrets[user_name]; ok {
		ctx.JSON(http.StatusOK, gin.H{"user": user_name, "secret": secret})
	} else {
		ctx.JSON(http.StatusBadRequest, gin.H{"user": user_name, "secret": "NO SECRET :("})
	}
}

2. REST API 端点的安全验证

https://github.com/pjebs/restgate

3. 用于从二进制数据提供静态文件的中间件/处理程

https://github.com/olebedev/staticbin

3. CORS

官方中间件

https://github.com/gin-contrib/cors

4. CSRF

https://github.com/utrack/gin-csrf

5. 报告的中间件

# 通过gocraft/health报告的中间件 
https://github.com/utrack/gin-health


# gocraft/health
https://github.com/gocraft/health

6. 带有上下文的漂亮打印错误的中间件

https://github.com/utrack/gin-merry

# 打印
https://github.com/ansel1/merry

7. 用于Gin框架的修订中间件

https://github.com/appleboy/gin-revision-middleware

8. 用于Gin框架的JWT中间件

https://github.com/appleboy/gin-jwt

9. 基于mongodb和mysql的会话中间件

https://github.com/kimiazhu/ginweb-contrib/tree/master/sessions

10. 用于公开服务器的主机名和方案的中间件

https://github.com/gin-contrib/location

11. 紧急恢复中间件,可让您构建更好的用户体验

https://github.com/ekyoung/gin-nice-recovery

12. 限制同时请求;可以帮助增加交通流量

https://github.com/aviddiviner/gin-limit

13. 一种内存中的中间件,用于通过自定义键和速率限制访问速率

https://github.com/yangxikun/gin-limit-by-key

14. gin简单模板包装

https://github.com/michelloworld/ez-gin-template

15. gin中间件Hydra

https://github.com/janekolszak/gin-hydra


# Hydra
https://github.com/ory/hydra

16. 旨在替代Gin的默认日志

https://github.com/szuecs/gin-glog

17. 用于通过Go-Monitor公开指标

https://github.com/szuecs/gin-gomonitor

18. 用于OAuth2

https://github.com/zalando/gin-oauth2

19. gin框架的替代静态资产处理程序。

https://github.com/hyperboloide/static

20. XssMw是一种中间件,旨在从用户提交的输入中“自动删除XSS”

https://github.com/dvwright/xss-mw

21. 简单的安全中间件集合。

https://github.com/danielkov/gin-helmet

22. 提供JWT / Session / Flash的中间件,易于使用,同时还提供必要的调整选项。也提供样品。

https://github.com/ScottHuangZL/gin-jwt-session

23. 用于gin框架的html / template易于使用。

https://github.com/foolin/gin-template

24. 基于IP地址的请求限制器。它可以与redis和滑动窗口机制一起使用。

https://github.com/imtoori/gin-redis-ip-limiter

25. _method受Ruby的同名机架启发而被POST形式参数覆盖的方法

https://github.com/bu/gin-method-override

26. limit-通过指定允许的源CIDR表示法的访问控制中间件。

https://github.com/bu/gin-access-limit

27. 用于Gin的Session中间件

https://github.com/go-session/gin-session

28. 轻量级和有用的请求指标中间件

https://github.com/semihalev/gin-stats

29. 向statsd守护进程报告的Gin中间件

https://github.com/amalfra/gin-statsd

30. check-用于Gin的健康检查中间件

https://github.com/RaMin0/gin-health-check

31. 一个有效,安全且易于使用的Go Session库

https://github.com/go-session/gin-session

32. 漂亮的例外页面

https://github.com/kubastick/ginception

33 .用于调查http请求的Gin中间件。

https://github.com/fatihkahveci/gin-inspector

34. Gin中间件/处理程序,用于转储请求和响应的标头/正文。对调试应用程序非常有帮助。

https://github.com/tpkeeper/gin-dump

35. Gin Prometheus metrics exporter

https://github.com/zsais/go-gin-prometheus

36. Gin的Prometheus指标导出器

https://github.com/chenjiandongx/ginprom

37. Gin middleware to gather and store metrics using rcrowley/go-metrics

https://github.com/bmc-toolbox/gin-go-metrics

#  rcrowley/go-metrics
https://github.com/rcrowley/go-metrics

38. Gin 中间件/处理器自动绑定工具。通过像beego这样的注释路线来支持对象注册

https://github.com/xxjwxc/ginrpc
posted @ 2021-12-21 18:00  河图s  阅读(339)  评论(0编辑  收藏  举报