Loading

5_中间件.md

Gin中间件

来源: https://www.qfgolang.com/?special=ginkuangjia&pid=4095

中间件

​ 在web应用服务中,完整的一个业务处理在技术上包含客户端操作、服务器端处理、返回处理结果给客户端三个步骤。

​ 在实际的业务开发和处理中,会有更负责的业务和需求场景。一个完整的系统可能要包含鉴权认证、权限管理、安全检查、日志记录等多维度的系统支持。

​ 鉴权认证、权限管理、安全检查、日志记录等这些保障和支持系统业务属于全系统的业务,和具体的系统业务没有关联,对于系统中的所有业务都适用。

​ 由此,在业务开发过程中,为了更好的梳理系统架构,可以将上述描述所涉及的一些通用业务单独抽离并进行开发,然后以插件化的形式进行对接。这种方式既保证了系统功能的完整,同时又有效的将具体业务和系统功能进行解耦,并且,还可以达到灵活配置的目的。

​ 这种通用业务独立开发并灵活配置使用的组件,一般称之为"中间件",因为其位于服务器和实际业务处理程序之间。其含义就是相当于在请求和具体的业务逻辑处理之间增加某些操作,这种以额外添加的方式不会影响编码效率,也不会侵入到框架中。中间件的位置和角色示意图如下图所示:

img

HandleFunc

在gin中,中间件称之为middleware,中间件的类型定义如下所示:

type HandlerFunc func(*Context)

​ HandlerFunc是一个函数类型,接收一个Context参数。用于编写程序处理函数并返回HandleFunc类型,作为中间件的定义。

自定义中间件

中间件的类型是函数,有两条标准:

  • func函数
  • 返回值类型为HandlerFunc

为全局路由注册

// 定义中间件
func RequestInfos() gin.HandlerFunc {
    return func(context *gin.Context) {
        path := context.FullPath()
        method := context.Request.Method
        fmt.Println("请求Path:", path)
        fmt.Println("请求Method:", method)
    }
}

func main() {

    engine := gin.Default()
    // 注册一个全局中间件
    engine.Use(RequestInfos())

    engine.GET("/query", func(context *gin.Context) {
        context.JSON(200, map[string]interface{}{
            "code": 1,
            "msg":  context.FullPath(),
        })
    })
    engine.Run(":9000")
}

为某个路由单独注册

// 给/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!",
		})
	})

为路由组注册中间件

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

写法1:

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

写法2:

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

context.Next()

​ context.Next函数可以将中间件代码的执行顺序一分为二,Next函数调用之前的代码在请求处理之前之前,当程序执行到context.Next时,会中断向下执行,转而先去执行具体的业务逻辑,执行完业务逻辑处理函数之后,程序会再次回到context.Next处,继续执行中间件后续的代码。

func main() {
    engine := gin.Default()
    engine.Use(RequestInfos())
    engine.GET("/query", func(context *gin.Context) {
        fmt.Println(" 中间件的使用方法  ")
        context.JSON(404, map[string]interface{}{
            "code": 1,
            "msg":  context.FullPath(),
        })
    })
    engine.Run(":9000")
}

func RequestInfos() gin.HandlerFunc {
    return func(context *gin.Context) {
        path := context.FullPath()
        method := context.Request.Method
        fmt.Println("请求Path:", path)
        fmt.Println("请求Method:", method)
        context.Next()
        fmt.Println(context.Writer.Status())
    }
}

执行程序,输出结果如下:

请求Path: /query
请求Method: GET
 中间件的使用方法  
404

img

  1. 程序先执行①和②。

  2. 执行到③时,转而去执行业务处理程序。

  3. 返回到中间件中,执行④。

中间件注意事项

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()

posted @ 2022-05-27 14:40  nsfoxer  阅读(39)  评论(0编辑  收藏  举报