golang gin框架中间件的使用-使用流程、定义、用例及总结(全面)

1.middleware的使用流程

代码如下:

点击查看代码
package main

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

func main()  {
	r := router()
	r.Run(":8080")
}

func router() *gin.Engine {
	r := gin.Default()

	r.Use(testMiddleware1(), testMiddleware2())
	r.Use(testMiddleware3(), testMiddleware4())
	r.GET("/hello", hello)

	return r
}

func hello(c *gin.Context)  {
	c.JSON(http.StatusOK, gin.H{
		"msg": "response",
		"code": 0,
	})
}

// middleware
func testMiddleware1() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println(" -> testMiddleware1")

		c.Next()

		fmt.Println(" <- testMiddleware1")
	}
}

func testMiddleware2() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println(" -> testMiddleware2")

		c.Next()

		fmt.Println(" <- testMiddleware2")
	}
}

func testMiddleware3() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println(" -> testMiddleware3")

		c.Next()

		fmt.Println(" <- testMiddleware3")
	}
}

func testMiddleware4() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println(" -> testMiddleware4")

		c.Next()

		fmt.Println(" <- testMiddleware4")
	}
}

实际效果如下

[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    /hello                    --> main.hello (7 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] Listening and serving HTTP on :8080
 -> testMiddleware1
 -> testMiddleware2
 -> testMiddleware3
 -> testMiddleware4
 <- testMiddleware4
 <- testMiddleware3
 <- testMiddleware2
 <- testMiddleware1
[GIN] 2022/06/07 - 18:31:46 | 200 |       703.4µs |       127.0.0.1 | GET      "/hello"

从返回结果看,可知gin的中间的执行流程:

图转自: https://juejin.cn/post/7064770224515448840

2.在路由中使用中间件实现认证

加入session验证的中间件处理:
代码:

点击查看代码
package main

import (
	"fmt"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main()  {
	r := gin.Default()
	// set session first
	store := cookie.NewStore([]byte("secret"))
	r.Use(sessions.Sessions("sessionid", store))
	r.POST("/login", login)

	// only routers which after middleware will work
	r.Use(CheckSessionMiddleware1(), Middleware2())
	r.GET("/hello", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "auth success, hello router",
			"code": 0,
		})
	})

	r.Run(":8080")
}

// login
func login(c *gin.Context)  {
	session := sessions.Default(c)
	session.Set("hello", "world")
	session.Save()
	c.JSON(http.StatusOK, gin.H{
		"msg": "login success",
		"code": 0,
	})
}

// middleware
// check if has session or not, if no, return directly, and abort middleware
func CheckSessionMiddleware1() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("-> middleware1")
		if _, err := c.Cookie("sessionid"); err != nil {
			c.Abort()
			c.JSON(http.StatusOK, gin.H{
				"msg": "auth failed",
				"code": 1,
			})
			return
		}

		c.Next()
	}
}

func Middleware2() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("-> middleware2")

		c.Next()
	}
}

加入Abort()的执行流程:

图转自: https://juejin.cn/post/7064770224515448840

通过加入中间件,我们可以实现拦截请求,如果是非认证的接口,没有携带session,可以直接返回错误信息,携带session则继续请求处理。

3.总结

3.1 中间件的实现

在gin框架中,认为只要实现了下面的就是HandlerFunc,即可作为中间件使用

type HandlerFunc func(*Context)
// HandlerFunc defines the handler used by gin middleware as return value.

一般中间件的定义如下:

// middleware
func Middleware1() gin.HandlerFunc {
	return func(c *gin.Context) {
                // 此处定义请求的前处理 
		fmt.Println(" -> testMiddleware1")

		c.Next()
                
                // 此处定义请求的后续处理
		fmt.Println(" <- testMiddleware1")
	}
}

// 下面的这种定义也可以,不建议使用,注册时使用r.Use(Recovery)
// middleware Recovery
func Recovery(c *gin.Context)  {
	defer func() {
		if r := recover(); r != nil {
			log.Logger.Error("gin catch error: ", log.Any("gin catch err: ", r))
			c.JSON(http.StatusOK, response.FailMsg("internal server error"))
		}
	}()
	c.Next()
}

3.2 中间件在路由中的应用

接着在路由中注册中间件:

...
// 1.通过Use()注册
r.Use(Middleware1())

// 2.另一种实现,在注册路由中实现注册中间件
r.POST("/login", Middleware(), login)
...

通常我们在整个web应用中,如果在中间件的拦截处理时,需要提前返回的话,我们可以通过c.Abort()实现后续中间件无须处理,具体见usage:

// check if has session or not, if no, return directly, and abort middleware
func CheckSessionMiddleware1() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("-> middleware1")
		if _, err := c.Cookie("sessionid"); err != nil {
			c.Abort()
			c.JSON(http.StatusOK, gin.H{
				"msg": "auth failed",
				"code": 1,
			})
			return
		}

		c.Next()
	}
}

3.3 中间件的注册顺序

在gin框架中,如果某些中间件在请求路由的后面,name这些中间件对其前面的请求路由是不起作用的,举个例子:

...
  r.GET("hello", hello) // hello的请求是不会被下面的Middleware1/2拦截处理的
  r.Use(Middleware1())
  r.Use(Middleware2())
...
  r.POST("/register", register) // register的请求会被上面的Middleware1/2拦截处理
...

实际中间件在工作时,按照注册顺序,路由前处理顺序为由前往后,后处理则由后往前。

3.4 中间件的作用范围

根据中间件的作用范围,可以分为全局中间件,局部中间件。
全局中间件
顾名思义,作用范围是该中间件以下的路由都可以拦截。

...
  r.Use(Middleware1())
...

局部中间件
只作用于某个路由或某些路由,取决于哪些路由的注册。
单个路由

...
  r.POST("/register", Middleware1(), register) // Middleware1中间件只作用与register请求
...

有的时候,我们如果想对于某些路由组添加中间,又如何处理呢?
我们看看gin的源码:

通过代码,可以看到,Group也提供了Use()方法来对组路由设置中间件,是不是很方便呢。
组路由

...
  server := gin.Default()
  ...
  server.Group("/app/v1").Use(Middleware1())
...

posted on 2022-06-07 18:34  进击的davis  阅读(1352)  评论(0编辑  收藏  举报

导航