[go]gin中間件


Go Web輕量級框架Gin學習系列:中間件使用詳解

gin中間件使用

- 中間件的作用:
	請求到達http請求處理方法之前,攔截請求
		認證
		權限校驗
		限流
		數據過濾
		ip白名單
	處理完請求后,攔截響應,冰進行相應的處理
		統一添加響應頭
		數據過濾


- 中間件加的位置
	全局加
	路由組加
	路由明細加
- 默認使用了Logger(), Recovery()全局作用了兩個中間件.
r:=gin.Default()

func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}


- gin自帶默認有這些中間件
func BasicAuth(accounts Accounts) HandlerFunc
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc
func Bind(val interface{}) HandlerFunc //攔截請求參數並進行綁定
func ErrorLogger() HandlerFunc       //錯誤日志處理
func ErrorLoggerT(typ ErrorType) HandlerFunc //自定義類型的錯誤日志處理
func Logger() HandlerFunc //日志記錄
func LoggerWithConfig(conf LoggerConfig) HandlerFunc
func LoggerWithFormatter(f LogFormatter) HandlerFunc
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc
func Recovery() HandlerFunc
func RecoveryWithWriter(out io.Writer) HandlerFunc
func WrapF(f http.HandlerFunc) HandlerFunc //將http.HandlerFunc包裝成中間件
func WrapH(h http.Handler) HandlerFunc //將http.Handler包裝成中間件
//去除默認全局中間件
r := gin.New()//不帶中間件
- 全局中間件
func main() {
	r := gin.Default()

	r.Use(func(c *gin.Context) {
		fmt.Println("hello start")
	})

	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{"name": "m1"})
	})

	r.Run()
}


- 或者
func M1(c *gin.Context) {
	fmt.Println("hello start")
}

func main() {
	r := gin.Default()

	r.Use(M1)

	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{"name": "m1"})
	})

	r.Run()
}

- 路由分組中使用中間件

func main() {
	r := gin.Default()
	v1 := r.Group("/v1", gin.Logger(), gin.Recovery())
	{
		v1.GET("/", func(c *gin.Context) {
			c.JSON(200, gin.H{"name": "m1"})
		})
		v1.GET("/test", func(c *gin.Context) {
			c.JSON(200, gin.H{"name": "m1 test"})
		})
	}
	
	r.Run()
}

- 單個路由使用中間件

func main() {
	r := gin.Default()
	r.GET("/", gin.Recovery(), gin.Logger(), func(c *gin.Context) {
		c.JSON(200, gin.H{"name": "m1"})
	})
	r.Run()
}

- 自定義中間件

//方法1
func MyMiddleware(c *gin.Context) {
	//中間件邏輯
	fmt.Println("hello")
}

func main() {
	r := gin.Default()

	r.Use(MyMiddleware)

	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{"name": "m1"})
	})
	r.Run()
}



//方法2: 返回一個中間件函數

Gin框架自帶的中間件方法,都是返回HandlerFunc類型
type HandlerFunc func(*Context)


func MyMiddleware() func(c *gin.Context) {
	//自定義邏輯
	fmt.Println("requesting...") //中間件不打印
	//返回中間件
	return func(c *gin.Context) {  
		//中間件邏輯
		fmt.Println("test2")
	}
}


func main() {
	r := gin.Default()

	r.Use(MyMiddleware()) //加括號

	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{"name": "m1"})
	})
	r.Run()
}
- 值傳遞

func MyMiddleware(c *gin.Context) {
	c.Set("mykey", 10)
	c.Set("mykey2", "m1")
}
func main() {
	//自定義中間件
	r := gin.New()
	r.GET("", MyMiddleware, func(c *gin.Context) {
		mykey := c.GetInt("mykey") //我們知道設置進行的是整型,所以使用GetInt方法來獲取
		mykey2 := c.GetString("mykey2")
		c.JSON(200, gin.H{
			"mykey":  mykey,
			"mykey2": mykey2,
		})
	})
	r.Run()
}


//gin set get取參數
func (c *Context) Set(key string, value interface{})
//判斷key是否存在 c.Get
func (c *Context) Get(key string) (value interface{}, exists bool)

func (c *Context) GetBool(key string) (b bool)
func (c *Context) GetDuration(key string) (d time.Duration)
func (c *Context) GetFloat64(key string) (f64 float64)
func (c *Context) GetInt(key string) (i int)
func (c *Context) GetInt64(key string) (i64 int64)
func (c *Context) GetString(key string) (s string)
func (c *Context) GetStringMap(key string) (sm map[string]interface{})
func (c *Context) GetStringMapString(key string) (sms map[string]string)
func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string)
func (c *Context) GetStringSlice(key string) (ss []string)
func (c *Context) GetTime(key string) (t time.Time)

func (c *Context) MustGet(key string) interface{} //必須有, 否則panic
//gin set get取參數 將值放在了context的keys鍵上(源碼里context.go 叫: METADATA MANAGEMENT)

func main() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		c.Set("name", "mm")
		fmt.Printf("%#v\n", c)
		c.JSON(200, gin.H{"name": "m1"})
	})
	r.Run()
}

&gin.Context{
	writermem:gin.responseWriter{ResponseWriter:(*http.response)(0xc00024c1c0),
									size:-1,
									status:200
								},
	Request:(*http.Request)(0xc000208100),
	Writer:(*gin.responseWriter)(0xc00020c410),
	Params:gin.Params(nil),
	handlers:gin.HandlersChain{(gin.HandlerFunc)(0x8fbd50),
	(gin.HandlerFunc)(0x8fccc0),
	(gin.HandlerFunc)(0x8fe720)},
	index:2,
	fullPath:"/",
	engine:(*gin.Engine)(0xc000222280),
	Keys:map[string]interface {}{"name":"mm"},   //放在了這個map里
	Errors:gin.errorMsgs(nil),
	Accepted:[]string(nil),
	queryCac
	he:url.Values(nil),
	formCache:url.Values(nil)
}

- 攔截器

func MyMiddleware(c *gin.Context){
    //請求前邏輯
    c.Next()
    //請求后邏輯
}

- gin內置的幾個中斷用戶請求的方法: 返回200,但body里沒數據
func (c *Context) Abort()
func (c *Context) AbortWithError(code int, err error) *Error
func (c *Context) AbortWithStatus(code int)

func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) //中斷請求后,返回json格式的數據

// 示例1:
func MyMiddleware(c *gin.Context) {
	c.Set("key", 1000) //請求前
	c.Next()
	c.JSON(http.StatusOK, c.GetInt("key")) //請求后
}
func main() {
	r := gin.New()
	r.GET("test", MyMiddleware, func(c *gin.Context) {
		k := c.GetInt("key")
		c.Set("key", k+2000)
	})
	r.Run()
}

實例

gin.BasicAuth中間件

package main

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

//type HandlerFunc func(*Context)

// 模擬一些私人數據
var secrets = gin.H{
	"foo":    gin.H{"email": "foo@bar.com", "phone": "123433"},
	"austin": gin.H{"email": "austin@example.com", "phone": "666"},
	"lena":   gin.H{"email": "lena@guapa.com", "phone": "523443"},
}

func main() {

	r := gin.Default()

	r.GET("/", func(c *gin.Context) {
		c.JSON(200, secrets)
		//c.String(200, "index")
	})
	// 為/admin路由組設置auth
	// 路由組使用 gin.BasicAuth() 中間件
	// gin.Accounts 是 map[string]string 的一種快捷方式
	authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
		"foo":    "bar",
		"austin": "1234",
		"lena":   "hello2",
		"manu":   "4321",
	}))

	// /admin/secrets 端點
	// 觸發 "localhost:8080/admin/secrets
	authorized.GET("/secrets", func(c *gin.Context) {
		// 獲取用戶,它是由 BasicAuth 中間件設置的
		user := c.MustGet(gin.AuthUserKey).(string)
		if secret, ok := secrets[user]; ok {
			c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
		} else {
			c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
		}
	})

	// 監聽並在 0.0.0.0:8080 上啟動服務
	r.Run(":8080")
}

參考

一文讀懂HTTP Basic身份認證

- http base認證

str=base64(admin:123456)
//request請求頭部攜帶這個
Authorization: Basic YWRtaW46MTIzNDU2

- 步驟1:用戶訪問受限資源
GET /protected_docs HTTP/1.1
Host: 127.0.0.1:3000

- 步驟2:服務端返回401要求身份認證
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm=protected_docs

- 步驟3:用戶發送認證請求
GET /protected_docs HTTP/1.1
Authorization: Basic Y2h5aW5ncDoxMjM0NTY=

Authorization首部的格式為Basic base64(userid:password)實際代碼如下:
Buffer.from('chyingp:123456').toString('base64'); // Y2h5aW5ncDoxMjM0NTY=

- 步驟4:服務端驗證請求
服務端收到用戶的認證請求后,對請求進行驗證。驗證包含如下步驟:

    根據用戶請求資源的地址,確定資源對應的realm。
    解析 Authorization 請求首部,獲得用戶名、密碼。
    判斷用戶是否有訪問該realm的權限。
    驗證用戶名、密碼是否匹配。
一旦上述驗證通過,則返回請求資源。如果驗證失敗,則返回401要求重新認證,或者返回403(Forbidden)。

缺陷:
1.密碼明文傳輸
2.無法吊銷認證

中間件計算一次請求的耗時: c.Next前置-后置

package main

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

//type HandlerFunc func(*Context)
func StatCost() gin.HandlerFunc {
    return func(c *gin.Context) {
        t := time.Now()

        //可以設置一些公共參數
        c.Set("example", "12345")
        //等其他中間件先執行
        c.Next()
        //獲取耗時
        latency := time.Since(t)
        log.Printf("total cost time:%d us", latency/1000)
    }
}

func main() {
    //r := gin.New()
    r := gin.Default()
    r.Use(StatCost())

    r.GET("/", func(c *gin.Context) {
        example := c.MustGet("example").(string)

        // it would print: "12345"
        log.Println(example)
        c.JSON(http.StatusOK, gin.H{
            "message": "success",
        })
    })

    // Listen and serve on 0.0.0.0:8080
    r.Run()
}


免責聲明!

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



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