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

自作多情 提交于 2020-04-15 09:00:15

【推荐阅读】微服务还能火多久?>>>

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


// 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\n", 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

// RouterGroup is used internally to configure router, a RouterGroup is associated with
// a prefix and an array of handlers (middleware).
type RouterGroup struct {
    Handlers HandlersChain
    basePath string
    engine   *Engine
    root     bool
}

// GET is a shortcut for router.Handle("GET", path, handle).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
    return group.handle(http.MethodGet, 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{}) {
    c.Render(code, render.JSON{Data: obj})
}

 

// 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/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, "\n") {
            format += "\n"
        }
        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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!