• [Gin] 单文件极简 HTTP Server 流程分析 ( gin-gonic/gin )


    /**
    * example.go
    *
    * @link https://cnblogs.com/farwish
    */
    package main import
    "github.com/gin-gonic/gin" func main() {
       // 由于是外部调用包,所以必须含包名 gin. 作为前缀
       // Default 返回带有已连接 Logger 和 Recovery 中间件的 Engine 实例。
    r := gin.Default()

       // Engine 结构体中内嵌了 RouterGroup 结构体,即继承了 RouterGroup(其有成员方法 GET、POST、DELETE、PUT、ANY 等) r.GET(
    "/ping", func(c *gin.Context) {

         // 使用 context.go 提供的方法渲染 json
         // 关于 gin.H 可看这里:https://www.cnblogs.com/farwish/p/12628549.html c.JSON(
    200, gin.H{ "message": "pong", })
    })

      // 默认是 0.0.0.0:8080 端口,内部使用了 http.ListenAndServe(address, engine) r.Run("9090")
    // listen and serve on 0.0.0.0:9090 }

    https://sourcegraph.com/github.com/gin-gonic/gin/-/blob/gin.go
    // gin.go
    
    // Default returns an Engine instance with the Logger and Recovery middleware already attached.
    func Default() *Engine {
      // 打印 WARNING 信息,见 debug.go debugPrintWARNINGDefault()

      // 取得一个新的空 Engine 实例 engine :
    = New()

      // 添加路由的全局中间件 engine.Use(Logger(), Recovery())
    return engine } // Engine is the framework's instance, it contains the muxer, middleware and configuration settings. // Create an instance of Engine, by using New() or Default() type Engine struct { RouterGroup // Enables automatic redirection if the current route can't be matched but a // handler for the path with (without) the trailing slash exists. // For example if /foo/ is requested but a route only exists for /foo, the // client is redirected to /foo with http status code 301 for GET requests // and 307 for all other request methods. RedirectTrailingSlash bool // 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 // If enabled, the router checks if another method is allowed for the // current route, if the current request can not be routed. // If this is the case, the request is answered with 'Method Not Allowed' // and HTTP status code 405. // If no other Method is allowed, the request is delegated to the NotFound // handler. HandleMethodNotAllowed bool ForwardedByClientIP bool // #726 #755 If enabled, it will thrust some headers starting with // 'X-AppEngine...' for better integration with that PaaS. AppEngine bool // If enabled, the url.RawPath will be used to find parameters. UseRawPath bool // If true, the path value will be unescaped. // If UseRawPath is false (by default), the UnescapePathValues effectively is true, // as url.Path gonna be used, which is already unescaped. UnescapePathValues bool // Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm // method call. MaxMultipartMemory int64 // RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes. // See the PR #1817 and issue #1644 RemoveExtraSlash 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 } // 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, RemoveExtraSlash: 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 }


    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)
        if root == nil {
            root = new(node)
            root.fullPath = "/"
            engine.trees = append(engine.trees, methodTree{method: method, root: root})
        }
        root.addRoute(path, handlers)
    }
    
    
    // Run attaches the router to a http.Server and starts listening and serving HTTP requests.
    // It is a shortcut for http.ListenAndServe(addr, router)
    // Note: this method will block the calling goroutine indefinitely unless an error happens.
    func (engine *Engine) Run(addr ...string) (err error) {
        defer func() { debugPrintError(err) }()
    
        address := resolveAddress(addr)
        debugPrint("Listening and serving HTTP on %s
    ", address)
        err = http.ListenAndServe(address, engine)
        return
    }
    
    
    // Use attaches a global middleware to the router. ie. the middleware attached though Use() will be
    // included in the handlers chain for every single request. Even 404, 405, static files...
    // For example, this is the right place for a logger or error management middleware.
    func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
        engine.RouterGroup.Use(middleware...)
        engine.rebuild404Handlers()
        engine.rebuild405Handlers()
        return engine
    }

    https://sourcegraph.com/github.com/gin-gonic/gin/-/blob/routergroup.go

    // routergroup.go
    
    // 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()
    }
    
    // Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
    // For example, all the routes that use a common middleware for authorization could be grouped.
    func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
        return &RouterGroup{
            Handlers: group.combineHandlers(handlers),
            basePath: group.calculateAbsolutePath(relativePath),
            engine:   group.engine,
        }
    }
    
    // BasePath returns the base path of router group.
    // For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".
    func (group *RouterGroup) BasePath() string {
        return group.basePath
    }
    
    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()
    }
    
    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 (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
        return joinPaths(group.basePath, relativePath)
    }
    
    func (group *RouterGroup) returnObj() IRoutes {
        if group.root {
            return group.engine
        }
        return group
    }
    
    // Handle registers a new request handle and middleware with the given path and method.
    // The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
    // See the example code in GitHub.
    //
    // For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
    // functions can be used.
    //
    // This function is intended for bulk loading and to allow the usage of less
    // frequently used, non-standardized or custom methods (e.g. for internal
    // communication with a proxy).
    func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
        if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil {
            panic("http method " + httpMethod + " is not valid")
        }
        return group.handle(httpMethod, relativePath, handlers)
    }

    https://sourcegraph.com/github.com/gin-gonic/gin/-/blob/context.go

    // context.go
    
    // JSON serializes the given struct as JSON into the response body.
    // It also sets the Content-Type as "application/json".
    func (c *Context) JSON(code int, obj interface{}) {
      // render.JSON 见 github.com/gin-gonic/gin/render c.Render(code, render.JSON{Data: obj}) }
    // 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) } }

    https://sourcegraph.com/github.com/gin-gonic/gin/-/blob/render/render.go

    // render/render.go
    
    package render
    
    import "net/http"
    
    // Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
    type Render interface {
        // Render writes data with custom ContentType.
        Render(http.ResponseWriter) error
        // WriteContentType writes custom ContentType.
        WriteContentType(w http.ResponseWriter)
    }
    
    var (
        _ Render     = JSON{}
        _ Render     = IndentedJSON{}
        _ Render     = SecureJSON{}
        _ Render     = JsonpJSON{}
        _ Render     = XML{}
        _ Render     = String{}
        _ Render     = Redirect{}
        _ Render     = Data{}
        _ Render     = HTML{}
        _ HTMLRender = HTMLDebug{}
        _ HTMLRender = HTMLProduction{}
        _ Render     = YAML{}
        _ Render     = Reader{}
        _ Render     = AsciiJSON{}
        _ Render     = ProtoBuf{}
    )
    
    func writeContentType(w http.ResponseWriter, value []string) {
        header := w.Header()
        if val := header["Content-Type"]; len(val) == 0 {
            header["Content-Type"] = value
        }
    }

    https://sourcegraph.com/github.com/gin-gonic/gin/-/blob/render/json.go

    // rendor/json.go

    //
    JSON contains the given interface object. type JSON struct { Data interface{} } var jsonContentType = []string{"application/json; charset=utf-8"} var jsonpContentType = []string{"application/javascript; charset=utf-8"} var jsonAsciiContentType = []string{"application/json"} // Render (JSON) writes data with custom ContentType. func (r JSON) Render(w http.ResponseWriter) (err error) { if err = WriteJSON(w, r.Data); err != nil { panic(err) } return } // WriteContentType (JSON) writes JSON ContentType. func (r JSON) WriteContentType(w http.ResponseWriter) { writeContentType(w, jsonContentType) } // WriteJSON marshals the given interface object and writes it with custom ContentType. func WriteJSON(w http.ResponseWriter, obj interface{}) error { writeContentType(w, jsonContentType) jsonBytes, err := json.Marshal(obj) if err != nil { return err } _, err = w.Write(jsonBytes) return err }

    https://sourcegraph.com/github.com/gin-gonic/gin/-/blob/debug.go

    // debug.go
    
    func debugPrintWARNINGDefault() {
        if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
            debugPrint(`[WARNING] Now Gin requires Go 1.11 or later and Go 1.12 will be required soon.
    
    `)
        }
        debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
    
    `)
    }
    
    func debugPrintWARNINGNew() {
        debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production.
     - using env:    export GIN_MODE=release
     - using code:    gin.SetMode(gin.ReleaseMode)
    
    `)
    }
    
    func debugPrint(format string, values ...interface{}) {
        if IsDebugging() {
            if !strings.HasSuffix(format, "
    ") {
                format += "
    "
            }
            fmt.Fprintf(DefaultWriter, "[GIN-debug] "+format, values...)
        }
    }
    
    // IsDebugging returns true if the framework is running in debug mode.
    // Use SetMode(gin.ReleaseMode) to disable debug mode.
    func IsDebugging() bool {
        return ginMode == debugCode
    }
    
    
    Link: https://www.cnblogs.com/farwish/p/12701654.html
  • 相关阅读:
    python总结4
    python中if __name__ == '__main__': 的解析
    matlab学习1
    phpstorm xdebug环境搭建
    uniapp 直播跳转小程序组件
    vue中异步函数async和await的用法
    TFS 2010安装配置(Advance)失败记录
    WIN2003 SMTP Service issue
    WIN2003 ftp server权限设置
    Discuz 7.2 SC UTF8设置
  • 原文地址:https://www.cnblogs.com/farwish/p/12701654.html
Copyright © 2020-2023  润新知