goalng框架Gin中间件的c.Next()有什么作用?

感觉用不用效果一样的啊,看文档说是执行挂起程序,具体怎么好理解一些呢,每个中间件一定要用吗?

阅读 4.7k
评论
    9 个回答

    中间件可以理解为洋葱穿透。

    c.Next() 之前的操作是在 Handler 执行之前就执行;
    c.Next() 之后的操作是在 Handler 执行之后再执行;

    func Middleware(c *gin.Context) {
        fmt.Println("Hello Before;")
        
        c.Next()
        
        fmt.Println("Hello After;")
    }

    然后你在Handler中输出一些内容就能发现。Hello Before 在你的Handler之前就输出。 Hello After在之后输出。

    之前的操作一般用来做验证处理,访问是否允许之类的。
    之后的操作一般是用来做总结处理,比如格式化输出、响应结束时间,响应时长计算之类的。

      package main
      
      import (
          "fmt"
      
          "github.com/gin-gonic/gin"
      )
      
      func main() {
          r := gin.Default()
          r.GET("/ping", Ping)
          r.Use(PrintRequest)
          r.Use(PrintResponse)
          // add your handlers here
          r.Run()
      }
      
      func PrintRequest(c *gin.Context) {
          fmt.Println("this is request")
      }
      
      func PrintResponse(c *gin.Context) {
          now := time.Now()
          // 先执行handler下面中间件和handler
          c.Next()
          fmt.Printf("this is response, cost: %d",
              time.Since(now).Nanoseconds())
      }

      我们以上面的代码为例,加入了两个中间件 PrintRequest PrintResponse

      • PrintRequest: 正常打印就ok了,不需要依赖下面的中间件,也没有什么需要特出处理的,加入到中间件后,正常执行即可
      • PrintResponse: 这个就不一样了,我想要计算这个请求到底花费了多久,就需要先执行下面的中间件和handler,等他们都完成后,再回到 PrintResponse 这个中间件里,进行计时就ok了。 当你打印返回的response body的时候,也是一样的道理,中间件都会比handler先执行,但是没有handler,中间件怎么拿到 response body,这时候就要用到 c.Next() 先去 执行 余下的中间件和 handler,然后再回到我这个中间件里面

      c.Next 的主要作用大概就是这样了

        中间件用的,不要管啥挂不挂起的,我估计是翻译的锅。
        假如你用两个中间件

        // 打印请求处理事件
        func Middleware1(ctx *gin.Context) {
            start:=time.Now() // 记录开始时间
            ctx.Next() // 调用处理过程(会产生调用耗时)
            
            t:=time.Since(start) // 调用处理过程完毕后计算时间差
            fmt.Println(t) // 打印本次请求处理时间差
        }
        func Middleware2(ctx *gin.Context) {
            if(!servic e.CheckLogin(ctx)) { // 登录检测,未登录
                return // 直接return,该请求的处理结束
            }
            ctx.Next() // 登录检测通过,继续后续处理
        }

          c.Next() 是让调该Handler 执行下一个Handler. 否则下一个Handler就执行不了。

          gin.Get("/",func (ctx *gin.Context){
               
              fmt.Println(3)
              // 这个是最后一个,因此就不必要调用 ctx.Next() 了,这里是我们常用的控制器方法
          }, func(ctx *gin.Context){
          
              fmt.Println(2)
              ctx.Next()
              // 这里是我们的中间件
          }, func(ctx *gin.Context){
              fmt.Println(1)
              ctx.Next()
          })
          我这里传了3个handler. 顺序传入,但是会逆序执行。
          
          
          中间件的函数签名和执行控制器方法的签名一样,只是中间件一般都在控制器之前执行,所以叫中间件。。。
          
          

            c.next //使用Next,则内部会去执行该路由前的已注册的中间件

              说到底就是继续执行下一个的意思。 你可以尝试把c.Next去掉,看看会发生什么

                • 2
                • 新人请关照

                你写个类似用户认证的中间件就懂了,会用到c.Next c.Abort,你再看看这些代码就懂了,单纯在中间件打印一些信息用不用是无所谓的,因为本来中间件就会一层一层执行

                  可以看看源码中的解释,这个方法只在中间件中使用有意义,调用后马上执行各个 handler(这些 handler 中包含你注册在路由上的方法),最后会返回到中间件。

                  // Next should be used only inside middleware.
                  // It executes the pending handlers in the chain inside the calling handler.
                  // See example in GitHub.
                  func (c *Context) Next() {
                      c.index++
                      for s := int8(len(c.handlers)); c.index < s; c.index++ {
                          c.handlers[c.index](c)
                      }
                  }
                    • 2
                    • 新人请关照

                    理解是加上c.Next()后马上开始执行下一个中间件,同时也执行c.Next()后的代码。不加上c.Next()则要等自身的handler执行完成才开始下一个中间件。下面是对比代码输出:

                    func testCNext() {
                        r := gin.New()
                    
                        mid1 := func(c *gin.Context) {
                            start := time.Now()
                            fmt.Println("middleware1 start")
                            // 注释 or 不注释
                            c.Next() 
                            fmt.Println(time.Since(start))
                            fmt.Println("middleware1 ending")
                        }
                    
                        mid2 := func(c *gin.Context) {
                            fmt.Println("middleware2 start")
                            c.Next()
                            fmt.Println("middleware2 ending")
                        }
                    
                        r.Use(mid1, mid2)
                        r.GET("/", func(c *gin.Context) {
                            c.JSON(http.StatusOK, "hi")
                        })
                        r.Run()
                    }
                    
                    不注释mid1中的c.Next()输出
                    middleware1 start
                    middleware2 start
                    middleware2 ending
                    175.819µs
                    middleware1 ending
                    
                    注释mid1中的c.Next()输出
                    middleware1 start
                    13.139µs
                    middleware1 ending
                    middleware2 start
                    middleware2 ending
                      撰写回答

                      登录后参与交流、获取后续更新提醒