Gin是基于的GO
的net/http
库编写的,这个库本身就是天然并发的。运行函数Engine.run()
便是调用net/http
库的ListenAndServe
函数:
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
}
ListenAndServe
会对每个请求调用Handler
接口中的ServeHTTP
方法:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
func ListenAndServe(addr string, handler Handler) error
Handler
接口被Gin
实现了,实现内容不复杂:首先构建出Context
上下文,然后调用handleHTTPRequest
方法找到和Request
路径匹配的handler
并执行(handleHTTPRequest
方法还会执行中间件的handler,该方法是本文介绍的核心方法,见下文)。如下:
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)
}
上文中handleHTTPRequest
传入Context
上下文,它的作用是什么?
答:它其实就是一些函数封装,和保存着请求路径、参数、类型等HTTP
上下文信息。它会作为参数传入用户的请求处理函数中,用户使用它可以拥有很多便捷的功能,比如它封装了返回时的Content-Type
,若想返回Json body
只需调用c.JSON(status, bodyContent)
,避免了写一大堆代码。
type Context struct {
writermem responseWriter
Request *http.Request
Writer ResponseWriter
Params Params
// 中间件执行函数 + 用户执行函数
handlers HandlersChain
// handlers 的下标,以确定当前正在调用哪个执行函数
index int8
engine *Engine
...
}
中间件是什么?Gin
怎么加载中间件的?
Gin
的中间件也有很多,常见的:Logrus、JWT等。通过调用Engine
的Use
方法将中间件处理函数添加到路径分组中,即是添加到RouterGroup
的Handlers
切片中:
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
...
return engine
}
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
到了这里,中间件已被添加到RouterGroup
的handlers
了,它什么时候被调用?
其实上文提到handleHTTPRequest
方法便是调用中间件的地方,先上代码再讨论:
func (engine *Engine) handleHTTPRequest(c *Context) {
// something
...
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
if t[i].method != httpMethod {
continue
}
root := t[i].root
// handlers切片 = 各中间件的执行函数 + 该请求处理函数。
handlers, params, tsr := root.getValue(rPath, c.Params, unescape)
if handlers != nil {
c.handlers = handlers
c.Params = params
c.Next()
c.writermem.WriteHeaderNow()
return
}
// something
...
}
// something
...
}
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
通过Next()
可以看到,各个中间件的处理函数会被依次执行,而请求处理函数
是最后加入handlers
的,也就最后执行。这样也就实现了:在请求处理函数执行前,执行中间件自定义处理。
若想在请求处理函数执行后,执行中间件的自定义处理,怎么办?中间件自己调用Context
的Next()
函数即可,就能实现:中间件中Next()
之前的逻辑在请求处理函数
之前执行;Next()
之后的逻辑在请求处理函数
之后执行。
(刚从Java转Go,共同学习,一起进步,觉得有些许收获可以点波赞,若有不对之处谢谢留言指出)