2

Gin-middleware: A mechanism provided by the author of the Gin framework for developers to allow developers to customize the Hook function requested to be executed. The middleware function (or Hook function, hook function) is suitable for processing many repeated operations. Scenarios (such as login authentication, permission verification, data paging, time-consuming statistics, logging, etc.), if only the unique logic of a page is directly processed by the Handler function under the corresponding route,


Define middleware

Gin's middleware must be of type gin.HandlerFunc (that is, the function containing the request context parameter *gin.Context)

For example: the following indexHandler is a middleware:

 package main

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

func indexHandler(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "msg": "index",
    })
}
func main() {
    r := gin.Default()
    r.GET("/index", indexHandler)
    r.Run(":9090")
}

For another example, we can also define a middleware like this:

 package main

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

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

//定义一个中间件m1
func m1(c *gin.Context) {
    fmt.Println("this is middle-ware m1~")
}
func main() {
    r := gin.Default()
    //GET(relativePath string, handlers ...HandlerFunc)
    r.GET("/index", m1, indexHandler)
    r.Run(":9090")
}

c.Next()

But there is no practical meaning to write middleware like this. We can try to write a program timing function. Here is an introduction, c.Next() can call subsequent processing functions:

 package main

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

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

//定义一个中间件m1
func m1(c *gin.Context) {
    fmt.Println("this is middle-ware m1~")
    start := time.Now()
    c.Next() //调用后续的处理函数
    //c.Abort() //组织调用后续的处理函数
    cost := time.Since(start)
    fmt.Printf("cost:  %v\n ", cost)
    fmt.Println("m1 is done")
}
func main() {
    r := gin.Default()
    //GET(relativePath string, handlers ...HandlerFunc)
    r.GET("/index", m1, indexHandler)
    r.Run(":9090")
}

The results are as follows:


r.Use()

Of course, the original design of middleware is for more reuse, such as using the Use(middle_ware...) function for global registration. For example, I have several routes, and I want to call m1 timing when accessing these routes. The function of the device:

 package main

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

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

//定义一个中间件m1
func m1(c *gin.Context) {
    fmt.Println("this is middle-ware m1~")
    start := time.Now()
    c.Next() //调用后续的处理函数
    //c.Abort() //组织调用后续的处理函数
    cost := time.Since(start)
    fmt.Printf("cost:  %v\n ", cost)
    fmt.Println("m1 is done")
}
func main() {
    r := gin.Default()
    //全局注册中间件
    r.Use(m1)
    //GET(relativePath string, handlers ...HandlerFunc)
    r.GET("/index", indexHandler)
    r.GET("/game", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "msg": "shop",
        })
    })
    r.GET("/food", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "msg": "food",
        })
    })
    r.GET("/ad", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "msg": "ad",
        })
    })
    r.Run(":9090")
}

The results are as follows:

c.Abort()

Here is an introduction, c.Abort() can deprive all subsequent processing functions of the right to run, skip directly, the example is as follows:

 package main

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

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

//定义一个中间件m1
func m1(c *gin.Context) {
    fmt.Println("this is middle-ware m1~")
    start := time.Now()
    c.Next() //调用后续的处理函数
    //c.Abort() //组织调用后续的处理函数
    cost := time.Since(start)
    fmt.Printf("cost:  %v\n ", cost)
    fmt.Println("m1 is done")
}

//定义一个中间件m2
func m2(c *gin.Context) {
    fmt.Println("this is middle-ware m2~")
    c.Abort() //组织调用后续的处理函数
    fmt.Println("m2 is done")
}

func main() {
    r := gin.Default()
    //全局注册中间件
    r.Use(m1, m2)
    //GET(relativePath string, handlers ...HandlerFunc)
    r.GET("/index", indexHandler)
    r.GET("/game", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "msg": "shop",
        })
    })
    r.GET("/food", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "msg": "food",
        })
    })
    r.GET("/ad", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "msg": "ad",
        })
    })
    r.Run(":9090")
}

The running result is as follows:

Login authentication middleware

For another example, if we want to write a middleware for login authentication, the pseudo code can be written as:

 //定义一个登录中间件
func authMiddleware(c *gin.Context) {
    //if 是登录用户
    //  c.Next()
    //else
    //  c.Abort()
}

However, in order to make some preparations more convenient (such as connecting to the database, etc.), we usually recommend writing middleware in the form of closures:

 //定义一个登录中间件
func authMiddleware(doChck bool) gin.HandlerFunc {
    //写成闭包的形式,就可以在这里进行数据库的连接,或其他的准备工作
    return func(c *gin.Context) { //这里存放校验参数的逻辑
        if doChck{
            //if 是登录用户
            //  c.Next()
            //else
            //  c.Abort()
        } else {
            c.Next()
        }
    }
}

Incorporate this part of the code into the overall code as follows:

 package main

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

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

//定义一个中间件m1
func m1(c *gin.Context) {
    fmt.Println("this is middle-ware m1~")
    start := time.Now()
    c.Next() //调用后续的处理函数
    //c.Abort() //组织调用后续的处理函数
    cost := time.Since(start)
    fmt.Printf("cost:  %v\n ", cost)
    fmt.Println("m1 is done")
}

//定义一个中间件m2
func m2(c *gin.Context) {
    fmt.Println("this is middle-ware m2~")
    c.Abort() //组织调用后续的处理函数
    fmt.Println("m2 is done")
}

//定义一个登录中间件
func authMiddleware(doChck bool) gin.HandlerFunc {
    //写成闭包的形式,就可以在这里进行数据库的连接,或其他的准备工作
    return func(c *gin.Context) { //这里存放校验参数的逻辑
        if doChck {
            //if 是登录用户
            //  c.Next()
            //else
            //  c.Abort()
        } else {
            c.Next()
        }
    }
}

func main() {
    r := gin.Default()
    //全局注册中间件
    r.Use(m1, m2, authMiddleware(true))
    //GET(relativePath string, handlers ...HandlerFunc)
    r.GET("/index", indexHandler)
    r.GET("/game", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "msg": "shop",
        })
    })
    r.GET("/food", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "msg": "food",
        })
    })
    r.GET("/ad", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "msg": "ad",
        })
    })
    r.Run(":9090")
}

Of course, routing groups can also be registered globally, for example:

 billGroup := r.Group("/bill")
    billGroup.Use(authMiddleware(true))
    {
        billGroup.GET("/index1", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{"msg": "lokaloka1"})
        })
        billGroup.GET("/index2", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{"msg": "lokaloka2"})
        })
        billGroup.GET("/index3", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{"msg": "lokaloka3"})
        })
    }

Communication between middleware c.Set() and c.Get()

For example, if you set a key-value pair in the middleware m2, and want to get the value according to the key in the indexHandler middleware, you can write:

 //定义一个中间件m2
func m2(c *gin.Context) {
    fmt.Println("this is middle-ware m2~")
    //c.Abort() //组织调用后续的处理函数
    c.Set("name", "lokays")
    fmt.Println("m2 is done")
}

func indexHandler(c *gin.Context) {
    fmt.Println("this is index")
    name, ok := c.Get("name")
    if !ok {
        name = "匿名用户"
    }
    c.JSON(http.StatusOK, gin.H{
        "msg": name,
    })
}

Notice

📢 Note: gin.Default() will automatically load Logger middleware and Recovery middleware:

  • Logger middleware will write logs to gin.DefaultWriter, whether or not GIN_Mode is configured
  • The Recovery middleware will recover all panics, and if there are panics, it will write a 500 response code

So, if you don't want to use any middleware, you can create a new route with gin.New().

  • When starting a new goroutine in middleware or Handler, the context's c *gin.Context cannot be used, only the read-only copy c.Copy() can be used

Reference: bilibili

LiberHome
409 声望1.1k 粉丝

有问题 欢迎发邮件 📩 liberhome@163.com