Gin框架中间件

Gin框架中间件

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

image-20211118214028476

一、定义中间件

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

package main

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

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

// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
	fmt.Println("m1 in ....")

	// 计算请求时间
	start :=time.Now()

	c.Next() // 调用后续处理的函数
	//c.Abort() // 阻止调用后续处理的函数
	end := time.Since(start)
	fmt.Printf("消耗时间time:%v\n", end)
	fmt.Println("m1 out ....")

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

	r.GET("/index", m1, index)
	r.Run(":9999")

}

image-20211118220119559

image-20211118220054724

二、定义局部中间件

在每次请求中添加中间件

package main

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

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

// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
	fmt.Println("m1 in ....")

	// 计算请求时间
	start :=time.Now()

	c.Next() // 调用后续处理的函数
	//c.Abort() // 阻止调用后续处理的函数
	end := time.Since(start)
	fmt.Printf("消耗时间time:%v\n", end)
	fmt.Println("m1 out ....")

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

	r.GET("/index", m1, index)
	// 局部中间件
	r.GET("/home", m1, func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg":"home",
		})
	})
	r.GET("/add", m1, func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg":"add",
		})
	})
	r.Run(":9999")

}

image-20211118220751236

三、定义全局中间件

package main

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

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

// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
	fmt.Println("m1 in ....")

	// 计算请求时间
	start := time.Now()

	c.Next() // 调用后续处理的函数
	//c.Abort() // 阻止调用后续处理的函数
	end := time.Since(start)
	fmt.Printf("消耗时间time:%v\n", end)
	fmt.Println("m1 out ....")

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

	// 全局注册中间
	r.Use(m1)
	r.GET("/index", index)
	// 局部中间件
	r.GET("/home", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "home",
		})
	})
	r.GET("/add", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "add",
		})
	})
	r.Run(":9999")

}

image-20211118220920806

三、定义多个中间件

package main

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

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

// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
	fmt.Println("m1 in ....")

	// 计算请求时间
	start := time.Now()

	c.Next() // 调用后续处理的函数
	//c.Abort() // 阻止调用后续处理的函数
	end := time.Since(start)
	fmt.Printf("消耗时间time:%v\n", end)
	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, m2)
	r.GET("/index", index)
	// 局部中间件
	r.GET("/home", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "home",
		})
	})
	r.GET("/add", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "add",
		})
	})
	r.Run(":9999")

}

image-20211118221347175

image-20211118221403410

image-20211118221650300

image-20211118221627386

四、Abort

package main

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

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

// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
	fmt.Println("m1 in ....")

	// 计算请求时间
	start := time.Now()

	c.Next() // 调用后续处理的函数
	end := time.Since(start)
	fmt.Printf("消耗时间time:%v\n", end)
	fmt.Println("m1 out ....")

}

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

	c.Abort() //  阻止调用后续处理的函数
	return // 后面m2将不会在执行
	fmt.Println("m2 out ....")

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

	// 全局注册中间
	r.Use(m1, m2)
	r.GET("/index", index)
	// 局部中间件
	r.GET("/home", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "home",
		})
	})
	r.GET("/add", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"msg": "add",
		})
	})
	r.Run(":9999")

}

image-20211118222205118

image-20211118222157465

image-20211118222407006

image-20211118222349970

中间件return语句

image-20211118222711540

image-20211118222639299

五、模拟认证

package main

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

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

// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
	fmt.Println("m1 in ....")

	// 计算请求时间
	start := time.Now()

	c.Next() // 调用后续处理的函数
	//c.Abort() // 阻止调用后续处理的函数
	end := time.Since(start)
	fmt.Printf("消耗时间time:%v\n", end)
	fmt.Println("m1 out ....")

}

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

	c.Next() // 调用后续处理的函数

	fmt.Println("m2 out ....")

}

// 认证中间件
//func authMiddleware(c *gin.Context) {
//	// 是否登录判断
//	username := c.Query("username")
//	// 判断是否登录用户
//	if username == "RandySun" {
//
//		c.Next()
//	} else {
//		// 认证失败
//		c.JSON(http.StatusUnauthorized, gin.H{
//			"msg": "没有权限",
//		})
//		c.Abort()
//	}
//}

// 通过闭包认证中间件
func authMiddleware(doCheck bool) gin.HandlerFunc {
	// 连接数据库
	// 或者其他准备工作
	return func(c *gin.Context) {
		// 是否登录判断
		username := c.Query("username")
		// 判断是否登录用户
		if doCheck {
			if username == "RandySun" && doCheck {
				c.Next()
			} else {
				// 认证失败
				c.JSON(http.StatusUnauthorized, gin.H{
					"msg": "没有权限",
				})
				c.Abort()
			}

		} else {
			// 放行认证
			c.Next()
		}

	}
}

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

	// 全局注册中间
	r.Use(m1, m2, authMiddleware(true))
	r.GET("/index", index)	
	r.Run(":9999")

}

image-20211118224430900

image-20211118224443593

六、为路由组注册中间件

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

写法1:

package main

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

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


// 通过闭包认证中间件
func authMiddleware(doCheck bool) gin.HandlerFunc {
	// 连接数据库
	// 或者其他准备工作
	return func(c *gin.Context) {
		// 是否登录判断
		username := c.Query("username")
		// 判断是否登录用户
		if doCheck {
			if username == "RandySun" && doCheck {
				c.Next()
			} else {
				// 认证失败
				c.JSON(http.StatusUnauthorized, gin.H{
					"msg": "没有权限",
				})
				c.Abort()
			}

		} else {
			// 放行认证
			c.Next()
		}

	}
}

func main() {
	r := gin.Default()
	// 为路由组添加中间件
	shopGroup := r.Group("/shop", authMiddleware(true))
	{
		shopGroup.GET("/index", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"mgs": "shop index",
			})
		})

	}

	r.Run(":9999")

}

写法2:

package main

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

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


// 通过闭包认证中间件
func authMiddleware(doCheck bool) gin.HandlerFunc {
	// 连接数据库
	// 或者其他准备工作
	return func(c *gin.Context) {
		// 是否登录判断
		username := c.Query("username")
		// 判断是否登录用户
		if doCheck {
			if username == "RandySun" && doCheck {
				c.Next()
			} else {
				// 认证失败
				c.JSON(http.StatusUnauthorized, gin.H{
					"msg": "没有权限",
				})
				c.Abort()
			}

		} else {
			// 放行认证
			c.Next()
		}

	}
}

func main() {
	r := gin.Default()
	shopGroup := r.Group("/shop")
    
	// 为路由组添加中间件
	shopGroup.Use(authMiddleware(true))
	{
		shopGroup.GET("/index", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{
				"mgs": "shop index",
			})
		})

	}
	r.Run(":9999")

}

七、中间件取值

package main

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

func index(c *gin.Context) {
	fmt.Println("index")
	name, _ := c.Get("name")
	// 跨中间件取值
	fmt.Println("获取在中间中设置的值name:", name)
	c.JSON(http.StatusOK, gin.H{
		"msg": "index",
	})
}

// 定义一个中间件m1统计请求的耗时时间
func m1(c *gin.Context) {
	fmt.Println("m1 in ....")

	// 计算请求时间
	start := time.Now()

	c.Next() // 调用后续处理的函数
	//c.Abort() // 阻止调用后续处理的函数
	end := time.Since(start)
	fmt.Printf("消耗时间time:%v\n", end)
	fmt.Println("m1 out ....")

}

func m2(c *gin.Context) {
	fmt.Println("m2 in ....")
	// 在中间中设置值
	c.Set("name", "randySun")

	c.Next() // 调用后续处理的函数
	fmt.Println("m2 out ....")

}

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

	// 全局注册中间
	r.Use(m1, m2)
	r.GET("/index", index)
	
	r.Run(":9999")

}

image-20211118230215271

八、中间件

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

为全局路由注册

func main() {
	// 新建一个没有任何默认中间件的路由
	r := gin.New()
	// 注册一个全局中间件
	r.Use(StatCost())
	
	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()
}

为某个路由单独注册

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

中间件注意事项

gin默认中间件

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

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

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

gin中间件中使用goroutine

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

九、运行多个服务

多个端口启动服务,例如:

package main

import (
	"log"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
)

var (
	g errgroup.Group
)

func router01() http.Handler {
	e := gin.New()
	e.Use(gin.Recovery())
	e.GET("/", func(c *gin.Context) {
		c.JSON(
			http.StatusOK,
			gin.H{
				"code":  http.StatusOK,
				"error": "Welcome server 01",
			},
		)
	})

	return e
}

func router02() http.Handler {
	e := gin.New()
	e.Use(gin.Recovery())
	e.GET("/", func(c *gin.Context) {
		c.JSON(
			http.StatusOK,
			gin.H{
				"code":  http.StatusOK,
				"error": "Welcome server 02",
			},
		)
	})

	return e
}

func main() {
	server01 := &http.Server{
		Addr:         ":8080",
		Handler:      router01(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	server02 := &http.Server{
		Addr:         ":8081",
		Handler:      router02(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
   // 借助errgroup.Group或者自行开启两个goroutine分别启动两个服务
	g.Go(func() error {
		return server01.ListenAndServe()
	})

	g.Go(func() error {
		return server02.ListenAndServe()
	})

	if err := g.Wait(); err != nil {
		log.Fatal(err)
	}
}
原文地址:https://www.cnblogs.com/randysun/p/15626706.html