- 提供func(c *gin.Context)类型的函数,编写中间件业务
- 调用Engine下的Use()方法或RouterGroup下的Use()方法注册全局或路由组中间件
- 执行时,通过Context的index当前中间件下标获取到对应的中间件执行
- 通过Context.Next方法获取下一个中间件执行
- 最终执行到main Handler,当main Handler执行完毕后依次流出中间件
- 或通过Context.Abort() 将index移动末尾,也就是最后一个中间件,跳出中间件链
- 初始化中间件,将中间件保存到RouterGroup的HandlersChain数组中
- 新建切片,获取RouterGroup的HandlersChain数组中保存的中间件函数,插入新切片的头部,再把用户自定义处理某路径下请求的handler插入到尾部,构建前缀树
func New() *Engine {
debugPrintWARNINGNew()
engine := &Engine{
//实例化默认的RouterGroup,其中Handlers为中间件数组
RouterGroup: RouterGroup{
Handlers: nil,
basePath: "/",
root: true,
},
FuncMap: template.FuncMap{},
RedirectTrailingSlash: true,
RedirectFixedPath: false,
HandleMethodNotAllowed: false,
ForwardedByClientIP: true,
AppEngine: defaultAppEngine,
UseRawPath: false,
RemoveExtraSlash: false,
UnescapePathValues: true,
MaxMultipartMemory: defaultMultipartMemory,
//trees 是最重要的点!!!!负责存储路由和handle方法的映射,采用类似字典树的结构
trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"},
secureJsonPrefix: "while(1);",
}`
engine.RouterGroup.engine = engine
//这里采用 sync/pool 实现context池,减少频繁context实例化带来的资源消耗
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
return engine
}
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
//获取Engine中默认的RouterGroup执行注册中间件逻辑
engine.RouterGroup.Use(middleware...)
engine.rebuild404Handlers()
engine.rebuild405Handlers()
return engine
}
// Use adds middleware to the group, see example code in GitHub.
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
type RouterGroup struct {
Handlers HandlersChain // 中间件函数执行链
basePath string // 路由组的基础路径
engine *Engine // Gin引擎对象
root bool // 是否为根路由组
}
// HandlersChain defines a HandlerFunc array.
type HandlersChain []HandlerFunc
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
return &RouterGroup{
Handlers: group.combineHandlers(handlers),
basePath: group.calculateAbsolutePath(relativePath),
engine: group.engine,
}
}
router.GET("/hello", getting)
……
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 {
//1.通过相对路径获取绝对路径
absolutePath := group.calculateAbsolutePath(relativePath)
//2.合并前面Handlers(例如前面添加到中间件函数等)
handlers = group.combineHandlers(handlers)
//2.将对absolutePath路径Get请求对应的处理方法(handlers)加入到引擎的路由中
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
finalSize := len(group.Handlers) + len(handlers)
if finalSize >= int(abortIndex) {
panic("too many handlers")
}
mergedHandlers := make(HandlersChain, finalSize)
copy(mergedHandlers, group.Handlers)
copy(mergedHandlers[len(group.Handlers):], handlers)
return mergedHandlers
}
- 接收请求
- 接收到请求后的路由匹配与执行
func (engine *Engine) Run(addr ...string) (err error) {
defer func() { debugPrintError(err) }()
if engine.isUnsafeTrustedProxies() {
debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
}
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
err = http.ListenAndServe(address, engine.Handler())
return
}
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
//调用Server下的ListenAndServe()
return server.ListenAndServe()
}
//1.
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
//调用Server下的Serve()
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
//2.该方法内部重会获取连接,设置连接状态,基于协程处理请求
func (srv *Server) Serve(l net.Listener) error {
//……
for {
rw, e := l.Accept()
//……
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
//基于协程
go c.serve(ctx)
}
}
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
//……
//通过 ServeHTTP(w, w.req) 处理请求
serverHandler{c.server}.ServeHTTP(w, w.req)
//……
}
在ServeHTTP中会基于Pool获取到Context,通过Context处理请求
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// 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)
}
- 在handleHTTPRequest方法中当获取到对应的树节点后,会获取到对应的Handlers列表,封装到Context中, 并且初始化Context的index属性,
func (engine *Engine) handleHTTPRequest(c *Context) {
httpMethod := c.Request.Method
path := c.Request.URL.Path
//……
//获取到当前请求对应的树
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
if t[i].method != httpMethod {
continue
}
root := t[i].root
//1.获取RouterGroup上的handlers,获取请求参数等等
handlers, params, tsr := root.getValue(path, c.Params, unescape)
if handlers != nil {
//将handlers设置到Context上
c.handlers = handlers
//设置请求参数
c.Params = params
//初始化index
c.Next()
c.writermem.WriteHeaderNow()
return
}
//……
func (c *Context) Next() {
c.index++
for s := int8(len(c.handlers)); c.index < s; c.index++ {
c.handlers[c.index](c)
}
}
- HandlersChain属性,内部存储了中间件函数执行链
- basePath属性: 当前路由组的基础路径(根节点是"/",在Group分组时对应的是basePath+分组路径)
- root 表示当前是否是跟路由的布尔属性
- Engine当前Gin引擎实例
- Engine 隐式的继承了RouterGroup,在调用New()函数初始化时,会给这个默认的RouterGroup进行赋值
- 调用Engine 上的User()方法时,内部或获取这个默认的RouterGroup调用RouterGroup上的Use()注册中间件,会将中间件函数保存到RouterGroup的HandlersChain中间件执行链中
- 在调用RouterGroup的Group()进行分组时可以注册分组中间件,查看Group函数,内部会创建一个新的RouterGroup,调用RouterGroup上的combineHandlers()方法生成一个新的handler切片,把新的中间件函数的handler插入到头部,把前面注册的某路径下请求的handler中间件插入到尾部
- 最终中间件函数按照注册的先后顺序保存到了RouterGroup的HandlersChain中
- 也会执行calculateAbsolutePath()获取到当前接口上对应的中间件函数
- 调用Engine上的addRoute()方法进行路由注册,最终将所有的handler包括中间件与接口函数,接口路径等维护路由关系封装为node结构变量,保存到Engine的trees属性上