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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM