Skip to content
Go back

Gin框架解析

Published:  at  11:00 AM

特性

Gin 对 net/http的封装·

gin的 gin.Enginenet/http的 Handler ,使用net/http提供的http服务,由实现路由注册匹配、请求链路处理

Gin 的 API

gin.Engine 结构

本质上是一个http Handler (实现了HTPServe方法)

type Engine struct {

    RouterGroup

    // RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a
    // handler for the path with (without) the trailing slash exists.
    RedirectTrailingSlash bool

    // RedirectFixedPath if enabled, the router tries to fix the current request path, if no
    // handle is registered for it.
    // First superfluous path elements like ../ or // are removed.
    // Afterwards the router does a case-insensitive lookup of the cleaned path.
    // If a handle can be found for this route, the router makes a redirection
    // to the corrected path with status code 301 for GET requests and 307 for
    // all other request methods.
    // For example /FOO and /..//Foo could be redirected to /foo.
    // RedirectTrailingSlash is independent of this option.
    RedirectFixedPath bool

    // HandleMethodNotAllowed if enabled, the router checks if another method is allowed for the
    HandleMethodNotAllowed bool

    // ForwardedByClientIP if enabled, client IP will be parsed from the request's headers that
    ForwardedByClientIP bool

    // AppEngine was deprecated.
    AppEngine bool

    // UseRawPath if enabled, the url.RawPath will be used to find parameters.
    UseRawPath bool

    // UnescapePathValues if true, the path value will be unescaped.
    UnescapePathValues bool

    // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.
    RemoveExtraSlash bool

    // RemoteIPHeaders list of headers used to obtain the client IP when
    RemoteIPHeaders []string

    // TrustedPlatform if set to a constant of value gin.Platform*, trusts the headers set by
    TrustedPlatform string

    // MaxMultipartMemory value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
    MaxMultipartMemory int64

    // UseH2C enable h2c support.
    UseH2C bool

    // ContextWithFallback enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() when Context.Request.Context() is not nil.
    ContextWithFallback bool

    delims           render.Delims

    secureJSONPrefix string

    HTMLRender       render.HTMLRender

    FuncMap          template.FuncMap

    allNoRoute       HandlersChain

    allNoMethod      HandlersChain

    noRoute          HandlersChain

    noMethod         HandlersChain

    pool             sync.Pool

    trees            methodTrees

    maxParams        uint16

    maxSections      uint16

    trustedProxies   []string

    trustedCIDRs     []*net.IPNet

}

gin.Engine

RouterGroup

type RouterGroup struct {

    Handlers HandlersChain

    basePath string

    engine   *Engine

    root     bool

}

RouterGroup 将前缀和处理链联系起来 bool 表示是否是前缀树的根节点 Handlers 是处理函数链路

gin.Default

  1. gin.Default
func Default(opts ...OptionFunc) *Engine {

    debugPrintWARNINGDefault()

    engine := New()

    engine.Use(Logger(), Recovery())

    return engine.With(opts...)

}
  1. New
func New(opts ...OptionFunc) *Engine {

    debugPrintWARNINGNew()

    engine := &Engine{

        RouterGroup: RouterGroup{

            Handlers: nil,

            basePath: "/",

            root:     true,

        },

        FuncMap:                template.FuncMap{},

        RedirectTrailingSlash:  true,

        RedirectFixedPath:      false,

        HandleMethodNotAllowed: false,

        ForwardedByClientIP:    true,

        RemoteIPHeaders:        []string{"X-Forwarded-For", "X-Real-IP"},

        TrustedPlatform:        defaultPlatform,

        UseRawPath:             false,

        RemoveExtraSlash:       false,

        UnescapePathValues:     true,

        MaxMultipartMemory:     defaultMultipartMemory,

        trees:                  make(methodTrees, 0, 9),

        delims:                 render.Delims{Left: "{{", Right: "}}"},

        secureJSONPrefix:       "while(1);",

        trustedProxies:         []string{"0.0.0.0/0", "::/0"},

        trustedCIDRs:           defaultTrustedCIDRs,

    }

    engine.RouterGroup.engine = engine

    engine.pool.New = func() any {

        return engine.allocateContext(engine.maxParams)

    }

    return engine.With(opts...)

}

Engine.Run

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
}

可以看到gin通过 net/http 来实现HTTP服务

Engine的ServeHTTP方法

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)
}

通过Context pool 中获取Context 逻辑实现在Engine.handleHTTPRequest

Engine的handleHTTPRequest方法

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
    for i, tl := 0, len(t); i < tl; i++ {
        if t[i].method != httpMethod {
            continue
        }
        root := t[i].root
        // Find route in tree
        value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
        if value.params != nil {
            c.Params = *value.params
        }
        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
    }
    if engine.HandleMethodNotAllowed {
        // According to RFC 7231 section 6.5.5, MUST generate an Allow header field in response
        // containing a list of the target resource's currently supported methods.
        allowed := make([]string, 0, len(t)-1)

        for _, tree := range engine.trees {
            if tree.method == httpMethod {
                continue
            }
            if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
                allowed = append(allowed, tree.method)
            }
        }

        if len(allowed) > 0 {
            c.handlers = engine.allNoMethod
            c.writermem.Header().Set("Allow", strings.Join(allowed, ", "))
            serveError(c, http.StatusMethodNotAllowed, default405Body)
            return
        }
    }
    c.handlers = engine.allNoRoute
    serveError(c, http.StatusNotFound, default404Body)
}

根据Method和URL来匹配相应的handlersChain,然后调用Context.Next执行对应Handlers 所有元信息都由Context携带

Context.Next方法

func (c *Context) Next() {
    c.index++
    for c.index < int8(len(c.handlers)) {
        c.handlers[c.index](c)
        c.index++
    }
}

执行相应方法完成服务响应


Suggest Changes

Previous Post
etl和elt
Next Post
Using nginx as HTTP load balancer