gin使用教程(一)
gin使用教程(二)shell
上篇教程主要講了gin的路由以及參數獲取,這篇主要講解gin的中間件。
中間件能夠在咱們接受到一個http請求時,在handle以前或者handle以後作一些處理。一般,在handle以前,咱們能夠經過中間件很方便地進行校驗,若是再handle以後,咱們能夠對response進行一些調整。bash
使用服務器
// 建立一個不包含中間件的路由器
gin.New()
// 使用自定義中間件或者gin提供的中間件
gin.use(gin.Logger())
複製代碼
代替curl
gin.Default()
複製代碼
其實gin默認使用了Logger和Recovery兩個中間件,而後也是在內部調用了New:async
// gin.go
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery()) // 使用了Logger和Recovery兩個中間件
return engine
}
複製代碼
咱們對這兩個中間件作一個簡單的瞭解:函數
func main() {
logfile, _ := os.Create("./logs/gin.log")
// 這裏將log輸出到指定文件
// 注意這個配置必定要在gin.Default()以前
gin.DefaultWriter = io.MultiWriter(logfile, os.Stdout)
router := gin.Default()
// 這裏分別使用兩個中間件
router.Use(gin.Logger())
router.Use(gin.Recovery())
router.POST("/test", func(context *gin.Context) {
var person Person
if err := context.ShouldBind(&person); err != nil {
context.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
context.JSON(http.StatusOK, gin.H{
"success": true,
})
})
router.Run(":3000")
}
複製代碼
要本身實現中間件,不妨先看一下官方定義的Recovery中間件是如何實現的便可。post
// recovery.go
這裏只要返回一個HandlerFunc類型便可
func Recovery() HandlerFunc {
return RecoveryWithWriter(DefaultErrorWriter)
}
// gin.go
HandlerFunc就是一個參數爲*context的函數
type HandlerFunc func(*Context) 複製代碼
看懂了中間件的大概思路,那麼咱們本身來手動實現一個。
咱們來寫一個IP鑑權的中間件,假設咱們的需求是隻有白名單中的ip才能夠訪問服務器,那麼咱們能夠這麼實現:測試
// ipauth.go
func Auth() gin.HandlerFunc {
return func(context *gin.Context) {
// 定義ip白名單
whiteList := []string{
"127.0.0.1",
}
ip := context.ClientIP()
flag := false
for _, host := range whiteList {
if ip == host {
flag = true
break
}
}
if !flag {
context.String(http.StatusNetworkAuthenticationRequired, "your ip is not trusted: %s", ip)
context.Abort()
}
}
}
複製代碼
// main.go
func main() {
router := gin.New()
router.Use(ipauth.Auth())
router.GET("/test", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"success": true,
})
})
router.Run(":3000")
}
複製代碼
測試實例:ui
// 若是你用localhost訪問ip會顯示爲::1。
// 致使your ip is not trusted。這是由於你的電腦開啓了ipv6支持,這是ipv6下的本地迴環地址的表示。
$ curl http://127.0.0.1:3000/test
{"success":true}
複製代碼
// 把whiteList中的127.0.0.1改爲127.0.0.2以後,咱們再試一下
$ curl http://127.0.0.1:3000/test
your ip is not trusted: 127.0.0.1
複製代碼
此外,咱們的中間件能夠不全局使用,而只針對部分的group:url
func main() {
router := gin.Default()
// 定義了group
authorized := router.Group("/auth", ipauth.Auth())
// 對上面這個group進行路由綁定
authorized.GET("/write", handle)
router.GET("/read", handle)
router.Run(":3000")
}
func handle(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"success": true,
})
}
複製代碼
測試實例
$ curl http://127.0.0.1:3000/auth/write
your ip is not trusted: 127.0.0.1
$ curl http://127.0.0.1:3000/read
{"success":true}
複製代碼
也能夠只針對單個路由:
func main() {
router := gin.Default()
// 註冊一個路由,使用了 middleware1,middleware2 兩個中間件
router.GET("/someGet", middleware1, middleware2, handler)
// 默認綁定 :8080
router.Run()
}
func handler(c *gin.Context) {
log.Println("exec handler")
}
func middleware1(c *gin.Context) {
log.Println("exec middleware1")
//你能夠寫一些邏輯代碼
// 執行該中間件以後的邏輯
c.Next()
}
func middleware2(c *gin.Context) {
log.Println("arrive at middleware2")
// 執行該中間件以前,先跳到流程的下一個方法
c.Next()
// 流程中的其餘邏輯已經執行完了
log.Println("exec middleware2")
//你能夠寫一些邏輯代碼
}
複製代碼
能夠看出,中間件的寫法和路由的 Handler 幾乎是同樣的,只是多調用c.Next()
。正是有個c.Next()
,咱們能夠在中間件中控制調用邏輯的變化,看下面的 middleware2 代碼。在 middleware2中,執行到 c.Next()
時,Gin 會直接跳到流程的下一個方法中,等到這個方法執行完後,纔會回來接着執行 middleware2 剩下的代碼。
因此請求上面註冊的路由 url /someGet ,請求先到達middleware1,而後到達 middleware2,但此時 middleware2調用了 c.Next()
,因此 middleware2的代碼並無執行,而是跳到了 handler ,等 handler執行完成後,跳回到 middleware2,執行 middleware2剩下的代碼。
因此咱們能夠在控制檯上看到如下日誌輸出:
exec middleware1
arrive at middleware2
exec handler
exec middleware2
複製代碼
在中間件或處理程序中啓動新的goroutine時,你不該該使用其中的原始上下文,你必須使用只讀副本(c.Copy()
)
func main() {
r := gin.Default()
r.GET("/long_async", func(c *gin.Context) {
// 建立要在goroutine中使用的副本
cCp := c.Copy()
go func() {
// simulate a long task with time.Sleep(). 5 seconds
time.Sleep(5 * time.Second)
// 這裏使用你建立的副本
log.Println("Done! in path " + cCp.Request.URL.Path)
}()
})
r.Run(":3000")
}
複製代碼