golang web框架——gin使用教程(二)

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
}
複製代碼

咱們對這兩個中間件作一個簡單的瞭解:函數

  • Logger中間件可讓咱們作打印的一些自定義配置
  • Recovery中間件可讓咱們從崩潰中恢復
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中使用中間件

此外,咱們的中間件能夠不全局使用,而只針對部分的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
複製代碼

在中間件中使用goroutines

在中間件或處理程序中啓動新的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")
}
複製代碼
相關文章
相關標籤/搜索