柚子快報(bào)激活碼778899分享:Gin中間件
柚子快報(bào)激活碼778899分享:Gin中間件
Gin框架允許開發(fā)者在處理請求的過程中,加入用戶自己的鉤子(Hook)函數(shù)。這個鉤子函數(shù)就叫中間件,中間件適合處理一些公共的業(yè)務(wù)邏輯,比如登錄認(rèn)證、權(quán)限校驗(yàn)、數(shù)據(jù)分頁、記錄日志、耗時統(tǒng)計(jì)等。
定義中間件
Gin中的中間件必須是一個gin.HandlerFunc類型。
記錄接口耗時的中間件
例如我們像下面的代碼一樣定義一個統(tǒng)計(jì)請求耗時的中間件。
// StatCost 是一個統(tǒng)計(jì)耗時請求耗時的中間件
func StatCost() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Set("name", "小王子") // 可以通過c.Set在請求上下文中設(shè)置值,后續(xù)的處理函數(shù)能夠取到該值
// 調(diào)用該請求的剩余處理程序
c.Next()
// 不調(diào)用該請求的剩余處理程序
// c.Abort()
// 計(jì)算耗時
cost := time.Since(start)
log.Println(cost)
}
}
記錄響應(yīng)體的中間件
我們有時候可能會想要記錄下某些情況下返回給客戶端的響應(yīng)數(shù)據(jù),這個時候就可以編寫一個中間件來搞定。
type bodyLogWriter struct {
gin.ResponseWriter // 嵌入gin框架ResponseWriter
body *bytes.Buffer // 我們記錄用的response
}
// Write 寫入響應(yīng)體數(shù)據(jù)
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b) // 我們記錄一份
return w.ResponseWriter.Write(b) // 真正寫入響應(yīng)
}
// ginBodyLogMiddleware 一個記錄返回給客戶端響應(yīng)體的中間件
// https://stackoverflow.com/questions/38501325/how-to-log-response-body-in-gin
func ginBodyLogMiddleware(c *gin.Context) {
blw := &bodyLogWriter{body: bytes.NewBuffer([]byte{}), ResponseWriter: c.Writer}
c.Writer = blw // 使用我們自定義的類型替換默認(rèn)的
c.Next() // 執(zhí)行業(yè)務(wù)邏輯
fmt.Println("Response body: " + blw.body.String()) // 事后按需記錄返回的響應(yīng)
}
跨域中間件cors
推薦使用社區(qū)的GitHub - gin-contrib/cors: Official CORS gin's middleware?庫,一行代碼解決前后端分離架構(gòu)下的跨域問題。
注意:?該中間件需要注冊在業(yè)務(wù)處理函數(shù)前面。
這個庫支持各種常用的配置項(xiàng),具體使用方法如下。
package main
import (
"time"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// CORS for https://foo.com and https://github.com origins, allowing:
// - PUT and PATCH methods
// - Origin header
// - Credentials share
// - Preflight requests cached for 12 hours
router.Use(cors.New(cors.Config{
AllowOrigins: []string{"https://foo.com"}, // 允許跨域發(fā)來請求的網(wǎng)站
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, // 允許的請求方法
AllowHeaders: []string{"Origin", "Authorization", "Content-Type"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
AllowOriginFunc: func(origin string) bool { // 自定義過濾源站的方法
return origin == "https://github.com"
},
MaxAge: 12 * time.Hour,
}))
router.Run()
}
當(dāng)然你可以簡單的像下面的示例代碼那樣使用默認(rèn)配置,允許所有的跨域請求。
func main() {
router := gin.Default()
// same as
// config := cors.DefaultConfig()
// config.AllowAllOrigins = true
// router.Use(cors.New(config))
router.Use(cors.Default())
router.Run()
}
注冊中間件
在gin框架中,我們可以為每個路由添加任意數(shù)量的中間件。
為全局路由注冊
func main() {
// 新建一個沒有任何默認(rèn)中間件的路由
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()
}
為某個路由單獨(dú)注冊
// 給/test2路由單獨(dú)注冊中間件(可注冊多個)
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!",
})
})
為路由組注冊中間件
為路由組注冊中間件有以下兩種寫法。
寫法1:
shopGroup := r.Group("/shop", StatCost())
{
shopGroup.GET("/index", func(c *gin.Context) {...})
...
}
寫法2:
shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{
shopGroup.GET("/index", func(c *gin.Context) {...})
...
}
中間件注意事項(xiàng)
gin默認(rèn)中間件
gin.Default()默認(rèn)使用了Logger和Recovery中間件,其中:
Logger中間件將日志寫入gin.DefaultWriter,即使配置了GIN_MODE=release。Recovery中間件會recover任何panic。如果有panic的話,會寫入500響應(yīng)碼。
如果不想使用上面兩個默認(rèn)的中間件,可以使用gin.New()新建一個沒有任何默認(rèn)中間件的路由。
gin中間件中使用goroutine
當(dāng)在中間件或handler中啟動新的goroutine時,不能使用原始的上下文(c *gin.Context),必須使用其只讀副本(c.Copy())。
運(yùn)行多個服務(wù)
我們可以在多個端口啟動服務(wù),例如:
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分別啟動兩個服務(wù)
g.Go(func() error {
return server01.ListenAndServe()
})
g.Go(func() error {
return server02.ListenAndServe()
})
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}
柚子快報(bào)激活碼778899分享:Gin中間件
文章來源
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。