一、概述
1、簡介
- Go官方為了支援Web開發,提供了
net/http
工具包;但是在實際專案中,團隊還是會選擇更加高效,更便捷的Web框架,如Gin
、Echo
,Beego
等; - 在這些團隊中,很多團隊選擇了
Gin
這個框架,它在net/http
的基礎上做了優化;對比其他主流框架,它更好的效能和更快的路由;
2、Gin的優點
-
快速:基於
Radix
樹的路由,效能非常強大。 -
支援中介軟體:內建許多中介軟體,如
Logger
,Gzip
,Authorization
等。 -
崩潰恢復:可以捕捉panic引發的程式崩潰,使Web服務可以一直執行。
-
JSON驗證:可以驗證請求中
JSON
資料格式。 -
路由分組:支援路由分組(
RouteGroup
),可以更方便組織路由。 -
錯誤管理機制:可以收集程式中的錯誤
-
多種資料渲染方式:支援
HTML
、JSON
、YAML
、XML
等資料格式的響應。 -
擴充套件性:非常簡單擴充套件中介軟體。
-
資料驗證器:支援資料驗證器且可以自定義。
3、Gin Simple Demo
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func setupRouter() *gin.Engine {
r := gin.Default()
// Ping test
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status" : 0,
"msg" : "success",
"data" : gin.H {
"content" : "pong",
},
})
})
return r
}
func main() {
r := setupRouter()
r.Run()
}
複製程式碼
二、Gin Simple Demo 解讀
從Demo Code可以看出,使用Gin的體驗非常順滑,定義處理Web請求就四步:匯入包、定義路由、編寫 Handler、監聽埠。
1. 匯入包
import "github.com/gin-gonic/gin"
複製程式碼
2. 定義路由
- gin.Engine是路由引擎,一般使用
gin.Default()
方法建立並返回gin.Engine例項
.
r := gin.Default() //r預設使用了Logger和Recovery兩個中介軟體
複製程式碼
說明:可以用gin.New()
方法建立並返回一個不包含任何中介軟體的gin.Engine例項
3. 編寫 Handler
通過預設路由,我們可以建立處理HTTP請求的方法,示例中使用GET方法:
// Ping test
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status" : 0,
"msg" : "success",
"data" : gin.H {
"content" : "pong",
},
})
})
複製程式碼
- Get方法底層實現是呼叫
RouterGroup
的handle
方法, 請求的處理是使用HandlerFunc
型別方法
// GET is a shortcut for router.Handle("GET", path, handle).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle("GET", relativePath, handlers)
}
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
複製程式碼
- 說明:Gin支援所有通用的HTTP請求方法,如
GET
,POST
,PUT
,PATCH
,OPTIONS
,HEAD
,DELETE
。
4. 監聽埠
- 定義好請求之後,使用Run()方法便可監聽埠,開始接受HTTP請求,如果Run()方法沒有傳入引數的話,則預設監聽8080埠。
r.Run()
複製程式碼
- Run方法的底層實現關鍵是:
http.ListenAndServe
,其中:http來自net/http
包。
func (engine *Engine) Run(addr ...string) (err error) {
defer func() { debugPrintError(err) }()
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
//
err = http.ListenAndServe(address, engine)
return
}
複製程式碼
三、Gin中重要資料結構
在Gin Simple Demo Code中,我們發現了三個重要的Go資料結構:gin.Engine
、gin.Context
和 gin.RouterGroup
1、gin.Engine
- gin.Engine 是框架的入口;我們通過 Engine 物件來定義服務路由資訊、組裝外掛、執行服務,整個 Web 服務的都是由它來驅動的。
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
// Create an instance of Engine, by using New() or Default()
type Engine struct {
RouterGroup
//....
}
複製程式碼
- gin.Engine本質是對內建的 HTTP 伺服器的包裝,讓它使用起來更加便捷。
- gin.Default() 函式會生成一個預設的 Engine 物件,裡面包含了 2 個預設的常用外掛,分別是 Logger 和 Recovery,Logger 用於輸出請求日誌,Recovery 確保單個請求發生 panic 時記錄異常堆疊日誌,輸出統一的錯誤響應(
推薦使用
)。
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
複製程式碼
- gin.New()函式會生成一個預設的 Engine 物件,不包含任何中介軟體的gin.Engine例項。
2、gin.Context
- 請求的上下文資訊物件,它是所有請求Handler的入口引數。
gin.Context
的結構定義如下:
// Context允許我們在中介軟體之間傳遞變數,管理流程,驗證請求的JSON,並返回JSON
type Context struct {
//請求物件
Request *http.Request
// 用來響應
writermem responseWriter
Writer ResponseWriter
// URL裡面的引數,比如:/xx/:id
Params Params
// 參與的處理者(中介軟體 + 請求處理者列表)
handlers HandlersChain
// 當前處理到的handler的下標
index int8
fullPath string
// Engine單例
engine *Engine
// 在context可以設定的值
Keys map[string]interface{}
// 一系列的錯誤
Errors errorMsgs
//為內容協商定義一組手動接受的格式。
Accepted []string
// queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query()
queryCache url.Values
// formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH,
// or PUT body parameters.
formCache url.Values
}
複製程式碼
- Context這個上下文物件不是每次生成,而是從物件池裡面取出來的;而請求的真正處理核心在
handleHTTPRequest
函式中。 - handleHTTPRequest函式中的核心邏輯:根據請求方法和請求的URI,找到並呼叫 處理的函式陣列(包括我們定義的處理函式,還有中間層的處理函式)。
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// 從物件池中獲取一個context物件
c := engine.pool.Get().(*Context)
// 初始化上下文物件,因為從物件池取出來的資料,有髒資料,故要初始化。
c.writermem.reset(w)
c.Request = req
c.reset()
//處理web請求 (http請求處理)
engine.handleHTTPRequest(c)
//將Context物件扔回物件池了
engine.pool.Put(c)
}
複製程式碼
3、gin.RouterGroup
- RouterGroup是路由分組,是對路由樹的包裝,所有的路由規則最終都是由它來進行管理。Engine 結構體繼承了 RouterGroup ,所以 Engine 直接具備了 RouterGroup 所有的路由管理功能。
// RouterGroup is used internally to configure router, a RouterGroup is associated with
// a prefix and an array of handlers (middleware).
type RouterGroup struct {
Handlers HandlersChain
basePath string
engine *Engine
root bool
}
// HandlersChain defines a HandlerFunc array.
type HandlersChain []HandlerFunc
複製程式碼
-
RouteGroup 物件中主要包括:
basePath
(字首路徑)、Engine 指標
和Handlers
(處理函式陣列)。 -
RouterGroup 實現了 IRouter 介面定義的一系列路由方法;這些方法最終都是通過呼叫 Engine.addRoute 方法將請求處理器掛接到路由樹中。
-
RouterGroup 內部有一個字首路徑屬性,它會將所有的子路徑都加上這個字首再放進路由樹中。有了這個字首路徑,就可以實現 URL 分組功能。Engine 物件內嵌的 RouterGroup 物件的字首路徑是 /,它表示根路徑。RouterGroup 支援分組巢狀,使用 Group 方法就可以讓分組下面再掛分組。
v1 := r.Group("/api/v1") { v1.POST("/submit",submit) v1.GET("/list",list) } // Engine物件中RouterGroup物件是第一層分組(根分組),v1是根分組的子分組。 複製程式碼
4、gin.H
- gin.H 是 map[string]interface{} 的一個快捷名稱.
type H map[string]interface{}
複製程式碼