gin 框架架構(gòu)及源碼解析

gin:gin是一個用golang編寫的web框架,它采用了類Martini的api,有更好的性能,速度快40倍,如果你需要非常好的性能,就使用gin吧
功能列表:

  • 鑒權(quán)(auth)
  • 上下文(context)、參數(shù)綁定(binding)、渲染(render)
  • 中間件(middleware)
  • 路由(routergroup、tree)
  • gin engine(Engine)
    核心文件及目錄結(jié)構(gòu):
gin/
  auth.go # 鑒權(quán)
  binding/ # 包含參數(shù)綁定的各種實現(xiàn),比如form表單、header頭、query參數(shù),json、xml、toml、yaml、protobuf等格式的body數(shù)據(jù)解析格式
  context.go # gin自定義的context實現(xiàn),
  gin.go # gin引擎的核心文件
  routergroup.go # 路由組的實現(xiàn),不再使用httprouter中的router,自己實現(xiàn)了routergroup
  tree.go # 基于radix tree的數(shù)據(jù)結(jié)構(gòu),用于保存路由信息,基于httprouter的實現(xiàn)(https://github.com/julienschmidt/httprouter)
  render/ # 數(shù)據(jù)渲染相關(guān),包含html、json、text、xml、yaml、protobuf等格式的數(shù)據(jù)渲染

其他文件及目錄

gin/
  deprecated.go # 不推薦使用的內(nèi)容放到這個文件下
  ginS # gin engine的單例實現(xiàn),一般不用于線上,在編寫腳本時可能用得上
  internal #  內(nèi)部的一些實現(xiàn),包含多個json庫的封裝,如jsoniter、sonic json等,bytes轉(zhuǎn)string、string轉(zhuǎn)bytes的函數(shù)封裝。比較簡單
  debug.go # debug相關(guān)
  errors.go # errors相關(guān)定義
  logger.go # 日志輸出實現(xiàn)
  mode.go # 多種運行模式
  recover.go # 異常恢復(fù)的捕捉
  response_writer.go # 封裝http ResponseWriter
  utils.go # 工具包

gin架構(gòu)設(shè)計


image.png

接下來分別從RouterGroup、Context、Engine來分析gin的核心源碼實現(xiàn)

RouterGroup.go

// 路由組、實現(xiàn)了IRouter接口
type RouterGroup struct {
    // Handlers 存儲了調(diào)用Use函數(shù)的中間件
    Handlers HandlersChain
    // 存儲路由組的url前綴
    basePath string
    // 存儲了engine的地址,在路由組中可以直接調(diào)用engine的addRoute方法和noRoute、noMethod HandlerChain
    engine   *Engine
    root     bool
}
type IRouter interface {
    IRoutes
    Group(string, ...HandlerFunc) *RouterGroup
}

// IRoutes defines all router handle interface.
type IRoutes interface {
    Use(...HandlerFunc) IRoutes

    Handle(string, string, ...HandlerFunc) IRoutes
    Any(string, ...HandlerFunc) IRoutes
    GET(string, ...HandlerFunc) IRoutes
    POST(string, ...HandlerFunc) IRoutes
    DELETE(string, ...HandlerFunc) IRoutes
    PATCH(string, ...HandlerFunc) IRoutes
    PUT(string, ...HandlerFunc) IRoutes
    OPTIONS(string, ...HandlerFunc) IRoutes
    HEAD(string, ...HandlerFunc) IRoutes
    Match([]string, string, ...HandlerFunc) IRoutes

    StaticFile(string, string) IRoutes
    StaticFileFS(string, string, http.FileSystem) IRoutes
    Static(string, string) IRoutes
    StaticFS(string, http.FileSystem) IRoutes
}
// 當(dāng)我們調(diào)用Group返回一個子路由組時,子路由組的Handlers繼承了父路由組的所有handlers,basePath 也添加了父路由組的路徑。所以父路由組的handlers都會作用于所有的子路由組中
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
    return &RouterGroup{
        Handlers: group.combineHandlers(handlers),
        basePath: group.calculateAbsolutePath(relativePath),
        engine:   group.engine,
    }
}
// 將middleware 追加到group的handlers中
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    group.Handlers = append(group.Handlers, middleware...)
    return group.returnObj()
}

// 通用方法、下面的Handle、GET、POST等都會調(diào)用handle
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    // 從group中計算basePath + relativePath,返回根路徑
    absolutePath := group.calculateAbsolutePath(relativePath)
    // 從group中返回所有middleware和傳進(jìn)來的handlers
    handlers = group.combineHandlers(handlers)
    // 調(diào)用engine的addRoute方法,將url和handlers寫入httprouter的前綴樹中
    group.engine.addRoute(httpMethod, absolutePath, handlers)
    return group.returnObj()
}

Context.go

type Context struct {
    // 對ResponseWriter的實現(xiàn)
    writermem responseWriter
    // 保留ServeHTTP的Request
    Request   *http.Request
    // 自定義的ResponseWriter接口、組合了http.ResponseWriter、http.Hijacker、http.Flusher接口,
    Writer    ResponseWriter
    // 存儲url的restful風(fēng)格的參數(shù):/index/:name/:id
    Params   Params
    // 存儲tree中當(dāng)前url的handlers鏈(包含middlewares和業(yè)務(wù)處理handler)
    handlers HandlersChain
    // 記錄Next函數(shù)處理Handlers鏈的位置
    index    int8
    // url的全路徑
    fullPath string
    // 保存engine的指針
    engine       *Engine
    // 保存從tree中通過getValue返回的url參數(shù),params的值會賦值給上面的Params
    params       *Params
    skippedNodes *[]skippedNode

    mu sync.RWMutex
    // 存儲context上下文數(shù)據(jù)
    Keys map[string]any

    Errors errorMsgs

    Accepted []string
    // 緩存c.Request.URL.Query()中的值
    queryCache url.Values
    // 緩存c.Request.PostForm中的數(shù)據(jù),包含POST、PATCH、PUT方法的body參數(shù)
    formCache url.Values

    sameSite http.SameSite
}

從sync.Pool中取出context后,做一些初始化的工作

func (c *Context) reset() {
    c.Writer = &c.writermem
    c.Params = c.Params[:0]
    c.handlers = nil
    c.index = -1

    c.fullPath = ""
    c.Keys = nil
    c.Errors = c.Errors[:0]
    c.Accepted = nil
    c.queryCache = nil
    c.formCache = nil
    c.sameSite = 0
    *c.params = (*c.params)[:0]
    *c.skippedNodes = (*c.skippedNodes)[:0]
}

由于context不是線程安全的,如果要在多個協(xié)程中使用context,可以調(diào)用封裝好的Copy函數(shù)

func (c *Context) Copy() *Context {}

Next函數(shù)用于遞歸調(diào)用Middleware,先執(zhí)行所有Middleware函數(shù)調(diào)用Next()之前的部分,再執(zhí)行業(yè)務(wù)邏輯的Handler,最后采用先進(jìn)后出的方式,依次執(zhí)行所有Middleware函數(shù)調(diào)用Next()之后的內(nèi)容。

func (c *Context) Next() {
    c.index++
    for c.index < int8(len(c.handlers)) {
        c.handlers[c.index](c)
        c.index++ // 這兒的index++,主要是為了解決,如果在Middleware中沒有調(diào)用Next()函數(shù),能夠保證程序能夠繼續(xù)執(zhí)行,而不是死循環(huán)
    }
}
// 將index設(shè)置為abortIndex,終止后續(xù)middleware和業(yè)務(wù)handler的執(zhí)行
func (c *Context) Abort() {
    c.index = abortIndex
}

Key相關(guān)的函數(shù)

// 設(shè)置key,keys用于存儲上下文數(shù)據(jù)。如果調(diào)用ShouldBindBodyWith方法,也會把body整個內(nèi)容緩存為一個key/value存儲到keys中
func (c *Context) Set(key string, value any) {}
func (c *Context) Get(key string) (value any, exists bool) {}
// 從Key中獲取對應(yīng)的string類型的值,其他類型的實現(xiàn)類似,不額外介紹
func (c *Context) GetString(key string) (s string) {
    if val, ok := c.Get(key); ok && val != nil {
        s, _ = val.(string)
    }
    return
}
func (c *Context) GetBool(key string) (b bool) {}
func (c *Context) GetInt(key string) (i int) {}
func (c *Context) GetInt64(key string) (i64 int64) {}
func (c *Context) GetUint(key string) (ui uint) {}
func (c *Context) GetUint64(key string) (ui64 uint64) {}
func (c *Context) GetFloat64(key string) (f64 float64) {}
func (c *Context) GetTime(key string) (t time.Time) {}
func (c *Context) GetDuration(key string) (d time.Duration) {}
func (c *Context) GetStringSlice(key string) (ss []string) {}
func (c *Context) GetStringMap(key string) (sm map[string]any) {}
func (c *Context) GetStringMapString(key string) (sms map[string]string) {}
func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) {}

Param 相關(guān)函數(shù)

// 獲取Param 參數(shù),Param參數(shù)從路由tree中解析出來的,如果路由參數(shù)中有多個相同key的數(shù)據(jù),只會返回第一個值
func (c *Context) Param(key string) string {
    return c.Params.ByName(key)
}
// 也可以手動設(shè)置param參數(shù)的值
func (c *Context) AddParam(key, value string) {
    c.Params = append(c.Params, Param{Key: key, Value: value})
}

Query參數(shù)相關(guān)函數(shù)

// 如果存在,從url query中返回值,否則返回"",是c.Request.URL.Query().Get(key)的簡寫
// 
//      GET /path?id=1234&name=Manu&value=
//         c.Query("id") == "1234"
//         c.Query("name") == "Manu"
//         c.Query("value") == ""
//         c.Query("wtf") == ""
func (c *Context) Query(key string) (value string) {
    value, _ = c.GetQuery(key)
    return
}

// 帶默認(rèn)值的Query
func (c *Context) DefaultQuery(key, defaultValue string) string {
    if value, ok := c.GetQuery(key); ok {
        return value
    }
    return defaultValue
}

// GetQuery 和 Query()類似, 多一個bool返回值,是Query()函數(shù)的底層調(diào)用
// 
//  GET /?name=Manu&lastname=
//  ("Manu", true) == c.GetQuery("name")
//  ("", false) == c.GetQuery("id")
//  ("", true) == c.GetQuery("lastname")
func (c *Context) GetQuery(key string) (string, bool) {
    if values, ok := c.GetQueryArray(key); ok {
        return values[0], ok
    }
    return "", false
}

// QueryArray 返回[]string
func (c *Context) QueryArray(key string) (values []string) {
    values, _ = c.GetQueryArray(key)
    return
}
// 初始化query緩存
func (c *Context) initQueryCache() {
    if c.queryCache == nil {
        if c.Request != nil {
            c.queryCache = c.Request.URL.Query()
        } else {
            c.queryCache = url.Values{}
        }
    }
}

// GetQueryArray 返回值帶[]string和bool
func (c *Context) GetQueryArray(key string) (values []string, ok bool) {
    c.initQueryCache() // 初始化query cache
    values, ok = c.queryCache[key]
    return
}

// QueryMap 返回字典值
func (c *Context) QueryMap(key string) (dicts map[string]string) {
    dicts, _ = c.GetQueryMap(key)
    return
}

// GetQueryMap 返回bool和字段值
func (c *Context) GetQueryMap(key string) (map[string]string, bool) {
    c.initQueryCache() // 初始化query cache
    return c.get(c.queryCache, key) // 返回map格式數(shù)據(jù)
}

// PostForm 從post中返回key
func (c *Context) PostForm(key string) (value string) {
    value, _ = c.GetPostForm(key)
    return
}

// DefaultPostForm 返回帶默認(rèn)值的key
func (c *Context) DefaultPostForm(key, defaultValue string) string {
    if value, ok := c.GetPostForm(key); ok {
        return value
    }
    return defaultValue
}

// GetPostForm 返回key值和bool
func (c *Context) GetPostForm(key string) (string, bool) {
    if values, ok := c.GetPostFormArray(key); ok {
        return values[0], ok
    }
    return "", false
}

// PostFormArray 返回切片類型的值
func (c *Context) PostFormArray(key string) (values []string) {
    values, _ = c.GetPostFormArray(key)
    return
}

// 初始化form 緩存
func (c *Context) initFormCache() {
    if c.formCache == nil {
        c.formCache = make(url.Values)
        req := c.Request
        if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
            if !errors.Is(err, http.ErrNotMultipart) {
                debugPrint("error on parse multipart form array: %v", err)
            }
        }
        c.formCache = req.PostForm
    }
}

// GetPostFormArray 返回切片類型值和bool
func (c *Context) GetPostFormArray(key string) (values []string, ok bool) {
    c.initFormCache()
    values, ok = c.formCache[key]
    return
}

// PostFormMap 返回字典類型的值
func (c *Context) PostFormMap(key string) (dicts map[string]string) {
    dicts, _ = c.GetPostFormMap(key)
    return
}

// GetPostFormMap 返回字典類型的值和bool
func (c *Context) GetPostFormMap(key string) (map[string]string, bool) {
    c.initFormCache()
    return c.get(c.formCache, key)
}

// 
func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) {
    dicts := make(map[string]string)
    exist := false
    for k, v := range m {
        if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key {
            if j := strings.IndexByte(k[i+1:], ']'); j >= 1 {
                exist = true
                dicts[k[i+1:][:j]] = v[0]
            }
        }
    }
    return dicts, exist
}

Query參數(shù)Binding

MustBind在綁定出錯時返回400錯誤
Bind在綁定出錯時不會返回錯誤

// Bind 會通過請求方法和content-type來判斷使用哪個類型的bind
func (c *Context) Bind(obj any) error {
    b := binding.Default(c.Request.Method, c.ContentType())
    return c.MustBindWith(obj, b)
}

// BindJSON 使用json解析body數(shù)據(jù)
func (c *Context) BindJSON(obj any) error {
    return c.MustBindWith(obj, binding.JSON)
}
// 同上
func (c *Context) BindXML(obj any) error {}
func (c *Context) BindQuery(obj any) error {}
func (c *Context) BindYAML(obj any) error {}
func (c *Context) BindTOML(obj any) error {}

// BindHeader 綁定header數(shù)據(jù)到obj對象
func (c *Context) BindHeader(obj any) error {
    return c.MustBindWith(obj, binding.Header)
}

// BindUri 將uri參數(shù)綁定到obj
func (c *Context) BindUri(obj any) error {
    if err := c.ShouldBindUri(obj); err != nil {
        c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck
        return err
    }
    return nil
}

// MustBindWith 使用提供的binding將數(shù)據(jù)綁定到obj,如果綁定出錯,返回400錯誤
func (c *Context) MustBindWith(obj any, b binding.Binding) error {
    if err := c.ShouldBindWith(obj, b); err != nil {
        c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck
        return err
    }
    return nil
}

// ShouldBind 
func (c *Context) ShouldBind(obj any) error {
    b := binding.Default(c.Request.Method, c.ContentType())
    return c.ShouldBindWith(obj, b)
}

// ShouldBindJSON 
func (c *Context) ShouldBindJSON(obj any) error {
    return c.ShouldBindWith(obj, binding.JSON)
}
func (c *Context) ShouldBindXML(obj any) error {}
func (c *Context) ShouldBindQuery(obj any) error {}
func (c *Context) ShouldBindYAML(obj any) error {}
func (c *Context) ShouldBindTOML(obj any) error {}
func (c *Context) ShouldBindHeader(obj any) error {}
func (c *Context) ShouldBindUri(obj any) error {}

// ShouldBindWith 使用提供的binding將request數(shù)據(jù)綁定到obj,出現(xiàn)錯誤不會返回400
func (c *Context) ShouldBindWith(obj any, b binding.Binding) error {
    return b.Bind(c.Request, obj)
}

// ShouldBindBodyWith 與 ShouldBindWith 類似, 但會body中的數(shù)據(jù)緩存到context的Keys中,供下次調(diào)用重用
// 注意: 該方法在綁定之前讀取body數(shù)據(jù)。所以如果只調(diào)用一次的話,使用ShouldBindWith會有更好的性能,
func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error) {
    var body []byte
    if cb, ok := c.Get(BodyBytesKey); ok {
        if cbb, ok := cb.([]byte); ok {
            body = cbb
        }
    }
    if body == nil {
        body, err = io.ReadAll(c.Request.Body)
        if err != nil {
            return err
        }
        c.Set(BodyBytesKey, body)
    }
    return bb.BindBody(body, obj)
}

Cookie相關(guān)函數(shù)

// 讀寫Cookie
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {}
func (c *Context) Cookie(name string) (string, error) {}

Render渲染相關(guān)函數(shù)

// Render 寫入響應(yīng)頭和調(diào)用Render來渲染數(shù)據(jù)
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 {
        // Pushing error to c.Errors
        _ = c.Error(err)
        c.Abort()
    }
}

// 使用http模板渲染數(shù)據(jù),內(nèi)部統(tǒng)一調(diào)用Render函數(shù)來渲染數(shù)據(jù)
func (c *Context) HTML(code int, name string, obj any) {} 
func (c *Context) IndentedJSON(code int, obj any) {} // 使用智能json
func (c *Context) SecureJSON(code int, obj any) {}
func (c *Context) JSONP(code int, obj any) {}
func (c *Context) JSON(code int, obj any) {}
func (c *Context) AsciiJSON(code int, obj any) {}
func (c *Context) PureJSON(code int, obj any) {}
func (c *Context) XML(code int, obj any) {}
func (c *Context) YAML(code int, obj any) {}
func (c *Context) TOML(code int, obj any) {}
func (c *Context) ProtoBuf(code int, obj any) {}
func (c *Context) String(code int, format string, values ...any) {}
// 重定向
func (c *Context) Redirect(code int, location string) {}

Negotiate

Negotiate主要用于根據(jù)客戶端提供的格式,協(xié)商返回對應(yīng)格式的數(shù)據(jù)

Engine.go

// HandlerFunc 定義了中間件和業(yè)務(wù)handler的函數(shù)
type HandlerFunc func(*Context)

// HandlersChain Handlers鏈,底層為handler的切片
type HandlersChain []HandlerFunc

// gin Engine
type Engine struct {
    // 組合了RouterGroup,所以可在engine中可以直接調(diào)用Group()、GET()、POST()等方法
    RouterGroup

    // 啟用自動重定向,如果當(dāng)前路由無法匹配,但存在帶(不帶)尾部斜杠的路徑處理程序。
    // 例如,如果請求 /foo/ 但僅存在 /foo 的路由,則對于 GET 請求,客戶端將重定向到 /foo,HTTP 狀態(tài)代碼為 301
    // 和 307 用于所有其他請求方法。
    RedirectTrailingSlash bool

    // 啟用路徑修復(fù),如果沒有已為其注冊的handler,則路由器嘗試修復(fù)當(dāng)前請求路徑。
    // 首先刪除多余的路徑元素,例如 ../ 或 // 。
    // 然后路由器對清理后的路徑進(jìn)行不區(qū)分大小寫的查找。
    // 如果可以找到該路由的句柄,則路由器進(jìn)行重定向到正確的路徑,
    // GET 請求的狀態(tài)代碼為 301, 所有其他請求方法返回的狀態(tài)代碼為 307 。
    // 例如 /FOO 和 /..//Foo 可以重定向到 /foo。
    // RedirectTrailingSlash 與此選項沒有關(guān)系。
    RedirectFixedPath bool

    // 如果啟用,如果當(dāng)前請求無法路由,路由器將檢查是否允許使用其他方法
    // 如果是這種情況,則請求將得到“不允許的方法”的響應(yīng)和 HTTP 405 狀態(tài)碼。
    // 如果沒有其他方法可路由,則返回 NotFound Handler
    HandleMethodNotAllowed bool
    UseRawPath bool
    UnescapePathValues bool
    // 移出額外的斜杠
    RemoveExtraSlash bool
    // 限制http.Request的 ParseMultipartForm 的maxMemory參數(shù)值
    MaxMultipartMemory int64
    // UseH2C 開啟http2協(xié)議,使用http2client.
    UseH2C bool
    delims           render.Delims
    secureJSONPrefix string
    HTMLRender       render.HTMLRender
    FuncMap          template.FuncMap
    allNoRoute       HandlersChain
    allNoMethod      HandlersChain
    noRoute          HandlersChain
    noMethod         HandlersChain
    // 存儲context的pool
    pool             sync.Pool
    // 使用壓縮前綴樹(radix tree) 存儲url和handler的對應(yīng)關(guān)系
    trees            methodTrees
    maxParams        uint16
    maxSections      uint16
    trustedProxies   []string
    trustedCIDRs     []*net.IPNet
}
// 驗證Engine實現(xiàn)了IRouter
var _ IRouter = (*Engine)(nil)
func New() *Engine {
    debugPrintWARNINGNew()
    engine := &Engine{
        RouterGroup: RouterGroup{
            Handlers: nil,
            basePath: "/",
            root:     true,
        },
        // ...
    }
    engine.RouterGroup.engine = engine
    // 配置pool 生成臨時對象的New函數(shù)
    engine.pool.New = func() any {
        return engine.allocateContext(engine.maxParams)
    }
    return engine
}

// Default 調(diào)用New函數(shù)初始化Engine,添加了Logger和Recover中間件
func Default() *Engine {
    debugPrintWARNINGDefault()
    engine := New()
    engine.Use(Logger(), Recovery())
    return engine
}
// context臨時對象的生成方法,在New函數(shù)中調(diào)用
func (engine *Engine) allocateContext(maxParams uint16) *Context {
    v := make(Params, 0, maxParams)
    skippedNodes := make([]skippedNode, 0, engine.maxSections)
    return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes}
}

// 將中間件添加到RouterGroup的Handler鏈中
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
    engine.RouterGroup.Use(middleware...)
    engine.rebuild404Handlers()
    engine.rebuild405Handlers()
    return engine
}

// 通過method、path和handler,將路由信息添加到tree中
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
    assert1(path[0] == '/', "path must begin with '/'")
    assert1(method != "", "HTTP method can not be empty")
    assert1(len(handlers) > 0, "there must be at least one handler")

    debugPrintRoute(method, path, handlers)

    root := engine.trees.get(method)
    // root為空,就生成新的root節(jié)點
    if root == nil {
        root = new(node)
        root.fullPath = "/"
        engine.trees = append(engine.trees, methodTree{method: method, root: root})
    }
    // 添加路由到tree中、tree中保存了多個壓縮前綴樹結(jié)構(gòu)(每個請求方法一個)
    root.addRoute(path, handlers)

    // Update maxParams
    if paramsCount := countParams(path); paramsCount > engine.maxParams {
        engine.maxParams = paramsCount
    }

    if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
        engine.maxSections = sectionsCount
    }
}

// 調(diào)用http.ListenAndServe 啟動服務(wù),使用不同的模式啟動服務(wù)
func (engine *Engine) Run(addr ...string) (err error) {}
func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {}
func (engine *Engine) RunUnix(file string) (err error) {}
func (engine *Engine) RunFd(fd int) (err error) {}
func (engine *Engine) RunListener(listener net.Listener) (err error) {}

// 實現(xiàn)了http.Handler接口,請求到達(dá)服務(wù)端后,會將請求交給ServeHTTP函數(shù)來處理
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    // 從pool中獲取一個context
    c := engine.pool.Get().(*Context)
    // 初始化ResponseWriter、Requet
    c.writermem.reset(w)
    c.Request = req
    c.reset()

    // handleHTTPRequest處理實際的請求
    engine.handleHTTPRequest(c)

    // 將context放回pool
    engine.pool.Put(c)
}

// 處理具體的請求
func (engine *Engine) handleHTTPRequest(c *Context) {
    httpMethod := c.Request.Method
    rPath := c.Request.URL.Path
    unescape := false
    if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
        rPath = c.Request.URL.RawPath
        unescape = engine.UnescapePathValues
    }

    if engine.RemoveExtraSlash {
        rPath = cleanPath(rPath)
    }

    // Find root of the tree for the given HTTP method
    t := engine.trees
    // 從trees中根據(jù)請求方法類型獲取對應(yīng)的tree
    for i, tl := 0, len(t); i < tl; i++ {
        if t[i].method != httpMethod {
            continue
        }
        root := t[i].root
        // 從tree中獲取handlers,并返回handlers和params
        value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
        if value.params != nil {
            c.Params = *value.params
        }
        // handlers不為空,調(diào)用Next()方法遞歸調(diào)用middleware和業(yè)處理handler
        if value.handlers != nil {
            c.handlers = value.handlers
            c.fullPath = value.fullPath
            c.Next()
            c.writermem.WriteHeaderNow()
            return
        }
        if httpMethod != http.MethodConnect && rPath != "/" {
            if value.tsr && engine.RedirectTrailingSlash {
                redirectTrailingSlash(c)
                return
            }
            if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
                return
            }
        }
        break
    }
    // 如果開啟了HandleMethodNotAllowed,就繼續(xù)遍歷tree,在其他tree中去匹配路由,如果匹配到后,就返回405錯誤
    if engine.HandleMethodNotAllowed {
        for _, tree := range engine.trees {
            if tree.method == httpMethod {
                continue
            }
            if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
                c.handlers = engine.allNoMethod
                serveError(c, http.StatusMethodNotAllowed, default405Body)
                return
            }
        }
    }
    // 返回404錯誤
    c.handlers = engine.allNoRoute
    serveError(c, http.StatusNotFound, default404Body)
}

到這里,gin的核心模塊context、routergroup、engine的實現(xiàn)就分析完了,radix tree的實現(xiàn)可以參考httprouter。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容