美文网首页
通过项目学习Go语言之 gin简析

通过项目学习Go语言之 gin简析

作者: whatiscoding | 来源:发表于2019-12-08 12:42 被阅读0次

    gin是go语言实现的一个http web框架,是一个类Martini的API且比其执行快40倍,gin支持插件模式加载在项目中需要的功能。在使用gin框架构建项目的时候,我们使用操作最多的就是request、response,gin为了方便使用设计了巧妙的context。
    下面我们看一个简单的基于gin构建的输出json数据的接口。

    func main() {
    
        r := gin.Default()
    
        r.GET("/ping", func(c *gin.Context) {
    
            c.JSON(200, gin.H{
    
                "message": "pong",
            })
    
        })
        fmt.Printf("come on")
        r.Run() // listen and serve on 0.0.0.0:8080 
    
    }
    

    运行后,通过浏览器访问127.0.0.1/ping,我们看到输出

    {
    message: "pong"
    }
    

    使用gin构建api服务就是如此简单。下面我们剖析一下gin的重要组成和一个http请求到达,解析到输出结果的整个流程。

    Engine

    从官方给出的例子的第一行代码说开来:r := gin.Default()
    通过这一行代码,我们初始换了一个Engine,Engine是整个gin的核心入口,承担着路由、中间件、参数等设置,是对http server的封装。
    通过gin.Default(),初始化一个默认的engine实例,它包含了日志和错误自恢复插件。

    // Default returns an Engine instance with the Logger and Recovery middleware already attached.
    func Default() *Engine {
        debugPrintWARNINGDefault()
        engine := New()
    //加载两个插件
        engine.Use(Logger(), Recovery())
        return engine
    }
    

    在Engine默认初始化方面中,调用的New()方法,我看看一下这个方法做了哪些工作:

    // 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,
            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
    }
    

    在上面也提到,Engine负责一系列的设置工作,通过初始化源代码可以看到,通过Engine可以进行很多默认参数的设置,在实际的项目中可以根据实际情况都默认参数进行合理设置。
    比如,在使用gin通过模板进行开发时,gin默认的delims为{{xxx}},这与vue的相冲突,可以通过下面代码来改变默认配置

    r.Delims("{[{", "}]}")
    

    在示例代码中,直接调用了r.Get 这个方法,其实Engine是实现了RouterGroup的,所以我们可以通过Engine直接进行路由的配置。gin对请求路径是使用了树形结构组织的,RouterGroup是对请求路径路由树的封装,项目中所有的路由规则都是有其来组织的。每次调用Get、Post等路由方法时,都会向路有树中添加注册路由,

    // GET is a shortcut for router.Handle("GET", path, handle).
    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 {
        absolutePath := group.calculateAbsolutePath(relativePath)
        handlers = group.combineHandlers(handlers)
        group.engine.addRoute(httpMethod, absolutePath, handlers)
        return group.returnObj()
    }
    

    通过RouterGroup可以很方便的对接口进行分组,比如将请求接口分为不通的版本,以实现平滑的接口升级。

    v1 := r.Group("/v1")
    
        {
            v1.GET("/ping",func(c *gin.Context){
                c.JSON(200, gin.H{
    
                    "message": " v1 pong",
                })
            })
        }
    
    

    Context

    在上面分析Engine初始化代码时,有这样一段代码:

    engine.RouterGroup.engine = engine
        engine.pool.New = func() interface{} {
            return engine.allocateContext()
        }
    

    这段代码是对gin中另一个重要的对象Context做池化的初始化代码,通过对Context进行池化处理,可以提高很大的效率。Engine作为gin的核心和入口,底层是对http server的封装,通过Engine实现的ServeHttp方法可以看到在此处对Context进行了初始化操作

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

    下面我们针对gin.Context做详细的讲解。打开Context的源码我们看到官方对Context的描述:Context是gin框架最重要的部分,通过context可以在组件间传递变量,管理路由,验证请求的json数据和渲染输出json数据。Context贯穿着整个的http请求,保存请求流程的上下文信息。
    首先我们看一下Context中定义的变量,

    type Context struct {
        writermem responseWriter
        Request   *http.Request
        Writer    ResponseWriter
    
        Params   Params
        handlers HandlersChain
        index    int8
        fullPath string
    
        engine *Engine
    
        // Keys is a key/value pair exclusively for the context of each request.
        Keys map[string]interface{}
    
        // Errors is a list of errors attached to all the handlers/middlewares who used this context.
        Errors errorMsgs
    
        // Accepted defines a list of manually accepted formats for content negotiation.
        Accepted []string
    
        // queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query()
        queryCache url.Values
    
        // formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH,
        // or PUT body parameters.
        formCache url.Values
    }
    

    Context对请求的参数、清理路径都进行了封装,可以很方便的直接或间接的操作http交互中的各种变量。
    同时,Context也内置了多种响应渲染形式,在项目构建过程中可以很方便的通过一行代码就完成json、xml等数据格式的输出。

    // Status sets the HTTP response code.
    func (c *Context) Status(code int) {...}
    
    // Header is a intelligent shortcut for c.Writer.Header().Set(key, value).
    // It writes a header in the response.
    // If value == "", this method removes the header `c.Writer.Header().Del(key)`
    func (c *Context) Header(key, value string) {...}
    
    // GetHeader returns value from request headers.
    func (c *Context) GetHeader(key string) string {...}
    .....
    
    
    // SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
    // The provided cookie must have a valid Name. Invalid cookies may be
    // silently dropped.
    func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {...}
    // PureJSON serializes the given struct as JSON into the response body.
    // PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
    func (c *Context) PureJSON(code int, obj interface{}) {...}
    
    // XML serializes the given struct as XML into the response body.
    // It also sets the Content-Type as "application/xml".
    func (c *Context) XML(code int, obj interface{}) {...}
    
    // YAML serializes the given struct as YAML into the response body.
    func (c *Context) YAML(code int, obj interface{}) {...}
    ...
    

    所有渲染组件最后还是通过http writer进行输出,

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

    HTML模板渲染

    gin内置了对html模板的渲染,使用方式也很简单,只需要指定模板的存放路径即可,

    func main() {
        router := gin.Default()
        router.LoadHTMLGlob("templates/*")
        //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
        router.GET("/index", func(c *gin.Context) {
            c.HTML(http.StatusOK, "index.tmpl", gin.H{
                "title": "Main website",
            })
        })
        router.Run(":8080")
    }
    

    emplates/index.tmpl

    <html>
        <h1>
            {{ .title }}
        </h1>
    </html>
    

    除了上面调到的各项超强的功能为,gin也为开发者提供静态文件服务、请求参数验证、文件上传等其他强大的功能,可以通过访问
    https://github.com/gin-gonic/gin
    去了解认识gin。

    本节完。

    相关文章

      网友评论

          本文标题:通过项目学习Go语言之 gin简析

          本文链接:https://www.haomeiwen.com/subject/qzxrgctx.html