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