gin是go語(yǔ)言實(shí)現(xiàn)的一個(gè)http web框架,是一個(gè)類Martini的API且比其執(zhí)行快40倍闯袒,gin支持插件模式加載在項(xiàng)目中需要的功能磨确。在使用gin框架構(gòu)建項(xiàng)目的時(shí)候,我們使用操作最多的就是request屯换、response,gin為了方便使用設(shè)計(jì)了巧妙的context与学。
下面我們看一個(gè)簡(jiǎn)單的基于gin構(gòu)建的輸出json數(shù)據(jù)的接口彤悔。
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
fmt.Printf("come on")
r.Run() // listen and serve on 0.0.0.0:8080
}
運(yùn)行后,通過(guò)瀏覽器訪問(wèn)127.0.0.1/ping,我們看到輸出
{
message: "pong"
}
使用gin構(gòu)建api服務(wù)就是如此簡(jiǎn)單索守。下面我們剖析一下gin的重要組成和一個(gè)http請(qǐng)求到達(dá)晕窑,解析到輸出結(jié)果的整個(gè)流程。
Engine
從官方給出的例子的第一行代碼說(shuō)開(kāi)來(lái):r := gin.Default()
通過(guò)這一行代碼卵佛,我們初始換了一個(gè)Engine杨赤,Engine是整個(gè)gin的核心入口,承擔(dān)著路由截汪、中間件疾牲、參數(shù)等設(shè)置,是對(duì)http server的封裝衙解。
通過(guò)gin.Default(),初始化一個(gè)默認(rèn)的engine實(shí)例阳柔,它包含了日志和錯(cuò)誤自恢復(fù)插件。
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
//加載兩個(gè)插件
engine.Use(Logger(), Recovery())
return engine
}
在Engine默認(rèn)初始化方面中蚓峦,調(diào)用的New()方法盔沫,我看看一下這個(gè)方法做了哪些工作:
// New returns a new blank Engine instance without any middleware attached.
// By default the configuration is:
// - RedirectTrailingSlash: true
// - RedirectFixedPath: false
// - HandleMethodNotAllowed: false
// - ForwardedByClientIP: true
// - UseRawPath: false
// - UnescapePathValues: true
func New() *Engine {
debugPrintWARNINGNew()
engine := &Engine{
RouterGroup: RouterGroup{
Handlers: nil,
basePath: "/",
root: true,
},
FuncMap: template.FuncMap{},
RedirectTrailingSlash: true,
RedirectFixedPath: false,
HandleMethodNotAllowed: false,
ForwardedByClientIP: true,
AppEngine: defaultAppEngine,
UseRawPath: false,
UnescapePathValues: true,
MaxMultipartMemory: defaultMultipartMemory,
trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"},
secureJsonPrefix: "while(1);",
}
engine.RouterGroup.engine = engine
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
return engine
}
在上面也提到医咨,Engine負(fù)責(zé)一系列的設(shè)置工作,通過(guò)初始化源代碼可以看到架诞,通過(guò)Engine可以進(jìn)行很多默認(rèn)參數(shù)的設(shè)置,在實(shí)際的項(xiàng)目中可以根據(jù)實(shí)際情況都默認(rèn)參數(shù)進(jìn)行合理設(shè)置谴忧。
比如很泊,在使用gin通過(guò)模板進(jìn)行開(kāi)發(fā)時(shí),gin默認(rèn)的delims為{{xxx}},這與vue的相沖突沾谓,可以通過(guò)下面代碼來(lái)改變默認(rèn)配置
r.Delims("{[{", "}]}")
在示例代碼中委造,直接調(diào)用了r.Get 這個(gè)方法,其實(shí)Engine是實(shí)現(xiàn)了RouterGroup的均驶,所以我們可以通過(guò)Engine直接進(jìn)行路由的配置昏兆。gin對(duì)請(qǐng)求路徑是使用了樹(shù)形結(jié)構(gòu)組織的,RouterGroup是對(duì)請(qǐng)求路徑路由樹(shù)的封裝妇穴,項(xiàng)目中所有的路由規(guī)則都是有其來(lái)組織的爬虱。每次調(diào)用Get、Post等路由方法時(shí)腾它,都會(huì)向路有樹(shù)中添加注冊(cè)路由跑筝,
// 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)
}
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath)
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
通過(guò)RouterGroup可以很方便的對(duì)接口進(jìn)行分組,比如將請(qǐng)求接口分為不通的版本瞒滴,以實(shí)現(xiàn)平滑的接口升級(jí)曲梗。
v1 := r.Group("/v1")
{
v1.GET("/ping",func(c *gin.Context){
c.JSON(200, gin.H{
"message": " v1 pong",
})
})
}
Context
在上面分析Engine初始化代碼時(shí),有這樣一段代碼:
engine.RouterGroup.engine = engine
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
這段代碼是對(duì)gin中另一個(gè)重要的對(duì)象Context做池化的初始化代碼妓忍,通過(guò)對(duì)Context進(jìn)行池化處理虏两,可以提高很大的效率。Engine作為gin的核心和入口世剖,底層是對(duì)http server的封裝定罢,通過(guò)Engine實(shí)現(xiàn)的ServeHttp方法可以看到在此處對(duì)Context進(jìn)行了初始化操作
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
下面我們針對(duì)gin.Context做詳細(xì)的講解。打開(kāi)Context的源碼我們看到官方對(duì)Context的描述:Context是gin框架最重要的部分搁廓,通過(guò)context可以在組件間傳遞變量引颈,管理路由耕皮,驗(yàn)證請(qǐng)求的json數(shù)據(jù)和渲染輸出json數(shù)據(jù)境蜕。Context貫穿著整個(gè)的http請(qǐng)求,保存請(qǐng)求流程的上下文信息凌停。
首先我們看一下Context中定義的變量粱年,
type Context struct {
writermem responseWriter
Request *http.Request
Writer ResponseWriter
Params Params
handlers HandlersChain
index int8
fullPath string
engine *Engine
// Keys is a key/value pair exclusively for the context of each request.
Keys map[string]interface{}
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
Errors errorMsgs
// Accepted defines a list of manually accepted formats for content negotiation.
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對(duì)請(qǐng)求的參數(shù)、清理路徑都進(jìn)行了封裝罚拟,可以很方便的直接或間接的操作http交互中的各種變量台诗。
同時(shí)完箩,Context也內(nèi)置了多種響應(yīng)渲染形式,在項(xiàng)目構(gòu)建過(guò)程中可以很方便的通過(guò)一行代碼就完成json拉队、xml等數(shù)據(jù)格式的輸出弊知。
// Status sets the HTTP response code.
func (c *Context) Status(code int) {...}
// Header is a intelligent shortcut for c.Writer.Header().Set(key, value).
// It writes a header in the response.
// If value == "", this method removes the header `c.Writer.Header().Del(key)`
func (c *Context) Header(key, value string) {...}
// GetHeader returns value from request headers.
func (c *Context) GetHeader(key string) string {...}
.....
// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
// The provided cookie must have a valid Name. Invalid cookies may be
// silently dropped.
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {...}
// PureJSON serializes the given struct as JSON into the response body.
// PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
func (c *Context) PureJSON(code int, obj interface{}) {...}
// XML serializes the given struct as XML into the response body.
// It also sets the Content-Type as "application/xml".
func (c *Context) XML(code int, obj interface{}) {...}
// YAML serializes the given struct as YAML into the response body.
func (c *Context) YAML(code int, obj interface{}) {...}
...
所有渲染組件最后還是通過(guò)http writer進(jìn)行輸出,
// Render writes the response headers and calls render.Render to render data.
func (c *Context) Render(code int, r render.Render) {
c.Status(code)
if !bodyAllowedForStatus(code) {
r.WriteContentType(c.Writer)
c.Writer.WriteHeaderNow()
return
}
if err := r.Render(c.Writer); err != nil {
panic(err)
}
}
HTML模板渲染
gin內(nèi)置了對(duì)html模板的渲染粱快,使用方式也很簡(jiǎn)單秩彤,只需要指定模板的存放路徑即可,
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")
//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
router.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "Main website",
})
})
router.Run(":8080")
}
emplates/index.tmpl
<html>
<h1>
{{ .title }}
</h1>
</html>
除了上面調(diào)到的各項(xiàng)超強(qiáng)的功能為事哭,gin也為開(kāi)發(fā)者提供靜態(tài)文件服務(wù)漫雷、請(qǐng)求參數(shù)驗(yàn)證、文件上傳等其他強(qiáng)大的功能鳍咱,可以通過(guò)訪問(wèn)
https://github.com/gin-gonic/gin
去了解認(rèn)識(shí)gin降盹。
本節(jié)完。