Gin框架系列03:換個姿勢理解中介軟體
什麼是中介軟體
中介軟體,英譯middleware,顧名思義,放在中間的物件,那麼放在誰中間呢?本來,客戶端可以直接請求到服務端介面。
現在,中介軟體橫插一腳,它能在請求到達介面之前攔截請求,做一些特殊處理,比如日誌記錄,故障處理等。這就是今天要講述的中介軟體,那麼,它在Gin框架中是怎麼使用的呢?
如何使用中介軟體
我們來看一下逢gin
必調的方法Default
,方法中有一個變數engine
,它Use
了Logger
和Recovery
兩個函式,這兩個函式就是gin
框架的日誌和故障處理中介軟體。
func Default() *Engine { debugPrintWARNINGDefault() engine := New() engine.Use(Logger(), Recovery()) return engine }
那就很清楚了,使用中介軟體就是呼叫Use
方法就行了唄,問題是現在除了這兩個中介軟體還能去Use
誰?不如咱先自己寫一箇中間件吧,這樣比較容易理解。
寫一箇中間件
寫啥呢,做產品講究MVP,那咱就寫個最簡單的閉環,攔截請求後輸出平也最帥
的日誌,產品就可以交付了。
寫之前先研究一下官方的Logger
和Recovery
是怎麼寫的,好比葫蘆畫瓢。
func Logger() HandlerFunc { return LoggerWithConfig(LoggerConfig{}) } func Recovery() HandlerFunc { return RecoveryWithWriter(DefaultErrorWriter) }
原來這兩個函式都返回了HandlerFunc
型別,那我們也模仿寫一個函式就好了。
func PingYe() gin.HandlerFunc {
return func(c *gin.Context) {
c.String(200, "平也最帥")
}
}
很簡單,寫完了,可以在main
函式中Use
它了。
func main() {
r := gin.Default()
r.Use(PingYe())
r.Run()
}
把專案跑起來,訪問localhsot:8080
看一下,帥呆了,成功渲染資料。
是不是太簡單了?這就交差了?我還能打十個啊!?
看來我要把畢生所學都交給你了。
延伸閱讀
Next
假如我們定義了兩個中介軟體,一個是平也最帥,另一個是在哪裡最帥。
func PingYe() gin.HandlerFunc {
return func(c *gin.Context) {
c.String(200, "平也最帥")
}
}
func Where() gin.HandlerFunc {
return func(c *gin.Context) {
c.String(200, "在全宇宙")
}
}
按順序把它們分別註冊到框架當中,這個時候我們猜測它會先輸出平也最帥
再輸出在全宇宙
對吧?對,確實是的。
func main() {
r := gin.Default()
r.Use(PingYe(), Where())
r.Run()
}
但是,如果我在不更改註冊順序的前提下,怎麼調換一下順序,先輸出在全宇宙
再輸出平也最帥
呢?這就用到了大名鼎鼎的Next
方法。它的作用就是先執行以下一個中介軟體,執行完了再回來繼續執行接下來的邏輯。記得是在中介軟體中呼叫哦~
func PingYe() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
c.String(200, "平也最帥")
}
}
Abort
當然,除了提供Next
方法外,理論上也應該有個中斷操作吧,畢竟拿中介軟體來做授權驗證的話,驗證失敗後還是希望阻斷請求的。所以,Abort
就是我們要找的那個方法。拿上面的例子,在平也最帥
的下一行呼叫Abort
方法後,Where
中介軟體就不再生效了,於是平也只剩下了單純的帥氣。
func PingYe() gin.HandlerFunc {
return func(c *gin.Context) {
c.String(200, "平也最帥")
c.Abort()
}
}
區域性中介軟體
剛才我講的中介軟體是會在所有的路由上生效的,有些不需要新增中介軟體的路由場景就無法適應了。所以,我們需要有能為區域性新增中介軟體的能力。
我們先實現給某個介面單獨加中介軟體,所以先得定義兩個介面know
與unknown
,分別代表認識平也與不認識兩個場景,鑑於認識後才知道平也是全宇宙最帥的,所以要綁中介軟體,不認識就算了。實現方式非常簡單,往路由後面的引數拼命加中介軟體就好了。
r.GET("know", PingYe(), Where())
r.GET("unknown", func(c *gin.Context) {
c.String(200, "???")
})
除了針對某個介面新增中介軟體外,還可以針對一組介面新增,同樣呼叫Use
方法即可。
v1 := r.Group("v1")
v1.Use(PingYe(), Where())
{
v1.GET("/know", func(c *gin.Context) {
c.String(200, "know")
})
v1.GET("/unknown", func(c *gin.Context) {
c.String(200, "unknown")
})
}
HTTP基本認證
基本認證,又稱BasicAuth
,加了基本認證的介面,會讓你在訪問介面時提供使用者名稱與密碼。
對於瀏覽器使用者,為了使用者的體驗會自動彈出登入框,而在其他場景下是沒有的,那在哪裡輸入賬號密碼呢?實際上,它是通過頭資訊傳輸的,頭資訊裡有一個固定的格式來代表基本認證。
Authorization: Basic <憑證>
憑證部分是是使用者名稱和密碼組合的base64
編碼,兩者以冒號方式拼接。然鵝,gin
框架的BaseAuth
中介軟體早已準備好了一切,它可以短短几行程式碼就能解析基本認證的資訊。
func main() {
r := gin.Default()
r.Use(gin.BasicAuth(gin.Accounts{
"pingye": "123",
}))
r.GET("/secrets", func(c *gin.Context) {
user := c.MustGet(gin.AuthUserKey).(string)
c.String(200, user+"已登入成功")
})
r.Run()
}
示例中的部分程式碼可能有些同學不太明白,比如BasicAuth
方法中的引數,因為基本認證需要賬號和密碼對吧,所以我們可以利用gin.Accounts
方便的配置好需要驗證的賬號密碼,gin.Accounts
是一個map型別,鍵代表使用者名稱,值代表密碼,當然可以設定不止一個鍵值對,根據你的喜好自行設定。
程式碼中還出現了c.MustGet
方法,這個方法的作用就是一定要取到某個引數,取不到就不幹了panic
,取的是什麼呢?就是gin.AuthUserKey
,在官方中的解釋是基本認證中使用者憑證的cookie
名稱。
// AuthUserKey is the cookie name for user credential in basic auth.
const AuthUserKey = "user"
Go語言庫示例開源專案「golang-examples」歡迎star~
https://github.com/pingyeaa/golang-examples
感謝大家的觀看,如果覺得文章對你有所幫助,歡迎關注公眾號「平也」,聚焦Go語言與技術原理。