Go语言的Gin框架快速入门篇

             Go语言的Gin框架快速入门篇

                                        作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

 

 

一.Gin框架概述

1>.Go语言的Web框架概述

  框架就是别人写好的代码我们可以直接使用,这个代码是专门针对一个开发方向定制的。例如,我们要做一个网站,利用框架就能非常块的完成网站的开发,如果没有框架,每个细节都需要我们处理,开发时间会大大降低。

  Go语言常见的web框架有beego,gin,echo,iris等等。值得一提的是,beego框架是咱们国人谢孟军开发的,其地位和Python的Django有点类似,而gin框架的地位和python的flask有点类似。

  综上所述,如果你要做纯web开发推荐大家使用beego,如果你仅仅是为了写一些后端API接口推荐大家使用gin框架,今天我们就一起来学习一下Gin框架。

  博主推荐阅读:
    https://beego.me/
    https://github.com/gin-gonic/gin

2>.Gin框架概述

  gin是使用golang开发的web框架.简单易用,高性能(是httprouter的40倍),适用于生产环境

  gin特点如下:
    快:
      路由使用基数树,低内存,不使用反射;     中间件注册:
      一个请求可以被一系列的中间件和最后的action处理     奔溃处理:
      gin可以捕获panic使应用程序可用     JSON校验:
      将请求的数据转换为JSON并校验     路由组:
      更好的组织路由的方式,无限制嵌套而不影响性能     错误管理:
      可以收集所有的错误     内建渲染方式:
      JSON,XML和HTML渲染方式     可继承:
      简单的去创建中间件

3>.Gin框架运行原理

  MVC模型如下所示:
    模型(Model):
      数据库管理与设计。
    控制器(Controller):
      处理用户输入的信息,负责从视图读取数据,控制用户输入,并向模型发送数据源,是应用程序中处理用户交互的部分,负责管理与用户交互控制。
    视图(View):
      将信息显示给用户。

  Gin框架的运行流程如下图所示。

4>.Gin和Beego框架对比

  MVC:
    Gin框架不完全支持,而beego完全支持。

  Web功能:
    Gin框架支持的不全面,比如Gin框架不是支持正则路由,不支持session。而beego支持的很全面。

  使用场景:
    Gin适合使用在封装API接口,而beego适合web项目。

5>.安装Gin组件

go get  github.com/gin-gonic/gin

6>.Hello World案例

package main

import "github.com/gin-gonic/gin"

func main() {
    /**
    所有的接口都要由路由来进行管理。
        Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求
        同时还有一个Any函数,可以同时支持以上的所有请求。

    创建路由(router)并引入默认中间件
        在源码中,首先是New一个engine,紧接着通过Use方法传入了Logger()和Recovery()这两个中间件。
        其中 Logger 是对日志进行记录,而 Recovery 是对有 painc时, 进行500的错误处理。

    创建路由(router)无中间件
        router := gin.New()
    */
    router := gin.Default()

    //定义路由的GET方法及响应的处理函数
    router.GET("/hello", func(c *gin.Context) {
        //将发送的消息封装成JSON发送给浏览器
        c.JSON(200, gin.H{
            //这是咱们定义的数据
            "message": "Hello World!",
        })
    })

    //启动路由并指定监听的地址及端口,若不指定默认监听0.0.0.0:8080
    router.Run("127.0.0.1:9000")
}
案例代码

 

二.Gin框架快速入门案例

1>.路由分组

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    /**
    所有的接口都要由路由来进行管理。
        Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求
        同时还有一个Any函数,可以同时支持以上的所有请求。

    创建路由(router)并引入默认中间件
        在源码中,首先是New一个engine,紧接着通过Use方法传入了Logger()和Recovery()这两个中间件。
        其中 Logger 是对日志进行记录,而 Recovery 是对有 painc时, 进行500的错误处理。

    创建路由(router)无中间件
        router := gin.New()
    */
    router := gin.Default()

    /**
    路由分组:
        在大型项目中,会经常使用到路由分组技术。
        路由分组有点类似于Django创建各种app,其目的就是将项目有组织的划分成多个模块。
    */
    //定义group1路由组
    group1 := router.Group("group1")
    {
        group1.GET("/login", func(context *gin.Context) {
            context.String(http.StatusOK, "<h1>Login successful</h1>")
        })
    }

    //定义group2路由组
    group2 := router.Group("group2")
    {
        group2.GET("/logout", func(context *gin.Context) {
            context.String(http.StatusOK, "<h3>Logout</h3>")
        })
    }

    //启动路由并指定监听的地址及端口,若不指定默认监听0.0.0.0:8080
    router.Run("127.0.0.1:9000")
}
案例代码

2>.获取GET方法参数

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    /**
    所有的接口都要由路由来进行管理。
        Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求
        同时还有一个Any函数,可以同时支持以上的所有请求。

    创建路由(router)并引入默认中间件
        在源码中,首先是New一个engine,紧接着通过Use方法传入了Logger()和Recovery()这两个中间件。
        其中 Logger 是对日志进行记录,而 Recovery 是对有 painc时, 进行500的错误处理。

    创建路由(router)无中间件
        router := gin.New()
    */
    router := gin.Default()

    router.GET("/blog", func(context *gin.Context) {
        //获取GET方法参数
        user := context.Query("user")
        //获取GET方法带默认值参数,如果没有则返回默认值"yinzhengjie"
        passwd := context.DefaultQuery("passwd", "yinzhengjie")
        //将获取到的数据返回给客户端
        context.String(http.StatusOK, fmt.Sprintf("%s:%s\n", user, passwd))
    })

    //启动路由并指定监听的地址及端口,若不指定默认监听0.0.0.0:8080
    router.Run("172.30.100.101:9000")
}
案例代码

3>.获取路径中的参数

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    /**
    所有的接口都要由路由来进行管理。
        Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求
        同时还有一个Any函数,可以同时支持以上的所有请求。

    创建路由(router)并引入默认中间件
        在源码中,首先是New一个engine,紧接着通过Use方法传入了Logger()和Recovery()这两个中间件。
        其中 Logger 是对日志进行记录,而 Recovery 是对有 painc时, 进行500的错误处理。

    创建路由(router)无中间件
        router := gin.New()
    */
    router := gin.Default()

    /**
    ":user"
        表示user字段必须存在,否则会报错404。
    "*passwd":
        表示action字段可以存在或不存在。
    */
    router.GET("/blog/:user/*passwd", func(context *gin.Context) {
        //获取路径中的参数
        user := context.Param("user")
        passwd := context.Param("passwd")
        //将获取到的数据返回给客户端
        context.String(http.StatusOK, fmt.Sprintf("%s:%s\n", user, passwd))
    })

    //启动路由并指定监听的地址及端口,若不指定默认监听0.0.0.0:8080
    router.Run("172.30.100.101:9000")
}
案例代码

4>.获取POST方法参数

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    /**
    所有的接口都要由路由来进行管理。
        Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求
        同时还有一个Any函数,可以同时支持以上的所有请求。

    创建路由(router)并引入默认中间件
        在源码中,首先是New一个engine,紧接着通过Use方法传入了Logger()和Recovery()这两个中间件。
        其中 Logger 是对日志进行记录,而 Recovery 是对有 painc时, 进行500的错误处理。

    创建路由(router)无中间件
        router := gin.New()
    */
    router := gin.Default()

    router.POST("/blog", func(context *gin.Context) {
        //从POST方法获取参数
        user := context.PostForm("user")
        //获取POST方法带默认值参数,如果没有则返回默认值"yinzhengjie"
        passwd := context.DefaultPostForm("passwd", "yinzhengjie")
        //将获取到的数据返回给客户端
        context.JSON(http.StatusOK, gin.H{
            "status": "POST",
            "USER":   user,
            "PASSWD": passwd,
        })
    })

    //启动路由并指定监听的地址及端口,若不指定默认监听0.0.0.0:8080
    router.Run("172.30.100.101:9000")
}
案例代码

5>.单文件上传

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
)

func main() {
    /**
    所有的接口都要由路由来进行管理。
        Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求
        同时还有一个Any函数,可以同时支持以上的所有请求。

    创建路由(router)并引入默认中间件
        在源码中,首先是New一个engine,紧接着通过Use方法传入了Logger()和Recovery()这两个中间件。
        其中 Logger 是对日志进行记录,而 Recovery 是对有 painc时, 进行500的错误处理。

    创建路由(router)无中间件
        router := gin.New()
    */
    router := gin.Default()

    // 给表单限制上传大小 (默认 32 MiB)
    // router.MaxMultipartMemory = 8 << 20  // 配置8MiB

    router.POST("/upload", func(c *gin.Context) {
        // 单文件
        file, _ := c.FormFile("file")
        log.Println(file.Filename)

        //底层采用流拷贝(io.Copy)技术,将上传文件到指定的路径
        //c.SaveUploadedFile(file, dst)

        c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
    })

    //启动路由并指定监听的地址及端口,若不指定默认监听0.0.0.0:8080
    router.Run("172.30.100.101:9000")

    /**
    使用curl命令测试:
        [root@yinzhengjie.com ~]# curl -X POST http://172.30.100.101:9000/upload   -F "file=@/root/dpt"   -H "Content-Type: multipart/form-data"

    */
}
案例代码

6>.多文件上传

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
)

func main() {
    /**
    所有的接口都要由路由来进行管理。
        Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求
        同时还有一个Any函数,可以同时支持以上的所有请求。

    创建路由(router)并引入默认中间件
        在源码中,首先是New一个engine,紧接着通过Use方法传入了Logger()和Recovery()这两个中间件。
        其中 Logger 是对日志进行记录,而 Recovery 是对有 painc时, 进行500的错误处理。

    创建路由(router)无中间件
        router := gin.New()
    */
    router := gin.Default()

    // 给表单限制上传大小 (默认 32 MiB)
    // router.MaxMultipartMemory = 8 << 20  // 配置8MiB

    router.POST("/upload", func(c *gin.Context) {
        // 多文件
        form, _ := c.MultipartForm()
        files := form.File["upload[]"]

        for _, file := range files {
            log.Println(file.Filename)
            //底层采用流拷贝(io.Copy)技术,将上传文件到指定的路径
            // c.SaveUploadedFile(file, dst)
        }
        c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
    })

    //启动路由并指定监听的地址及端口,若不指定默认监听0.0.0.0:8080
    router.Run("172.30.100.101:9000")

    /**
    使用curl命令测试:
        [root@yinzhengjie.com ~]# curl -X POST http://172.30.100.101:9000/upload   -F "upload[]=@/etc/issue"   -F "upload[]=@/etc/passwd"   -H "Content-Type: multipart/form-data"
    */
}
案例代码

7>.模型绑定

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

type Login struct {
    /**
    模型绑定:
        若要将请求主体绑定到结构体中,请使用模型绑定,目前支持JSON、XML、YAML和标准表单值(foo=bar&boo=baz)的绑定。
        需要在绑定的字段上设置tag,比如,绑定格式为json,需要这样设置 json:"fieldname" 。
        你还可以给字段指定特定规则的修饰符,如果一个字段用binding:"required"修饰,并且在绑定时该字段的值为空,那么将返回一个错。
        程序通过tag区分你传递参数的数据格式,从而自动解析相关参数.
    */
    User   string `form:"user" json:"user" xml:"user"  binding:"required"`
    Passwd string `form:"passwd" json:"passwd" xml:"passwd"  binding:"required"`
}

func main() {
    /**
    所有的接口都要由路由来进行管理。
        Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求
        同时还有一个Any函数,可以同时支持以上的所有请求。

    创建路由(router)并引入默认中间件
        在源码中,首先是New一个engine,紧接着通过Use方法传入了Logger()和Recovery()这两个中间件。
        其中 Logger 是对日志进行记录,而 Recovery 是对有 painc时, 进行500的错误处理。

    创建路由(router)无中间件
        router := gin.New()
    */
    router := gin.Default()

    router.POST("/login", func(context *gin.Context) {
        //定义接收请求的数据
        var login_user Login

        /**
        Gin还提供了两套绑定方法:
            Must bind:
                Methods - Bind, BindJSON, BindXML, BindQuery, BindYAML Behavior:
                    这些方法底层使用MustBindWith,如果存在绑定错误,请求将被以下指令中止 c.AbortWithError(400, err).SetType(ErrorTypeBind),
                    响应状态代码会被设置为400,请求头Content-Type被设置为text/plain; charset=utf-8。
                    注意,如果你试图在此之后设置响应代码,将会发出一个警告 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422。
                    如果你希望更好地控制行为,请使用ShouldBind相关的方法。

            Should bind
                Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML Behavior:
                    这些方法底层使用 ShouldBindWith,如果存在绑定错误,则返回错误,开发人员可以正确处理请求和错误。
                    当我们使用绑定方法时,Gin会根据Content-Type推断出使用哪种绑定器,如果你确定你绑定的是什么,你可以使用MustBindWith或者BindingWith。
        */
        err := context.ShouldBind(&login_user)
        //如果绑定出错了就将错误信息直接发送给前端页面.
        if err != nil {
            context.JSON(http.StatusBadRequest, gin.H{
                "Error": err.Error(),
            })
        }
        //将结构体绑定后,如果没有报错就可以解析到相应数据,此时我们验证用户名和密码,验证成功返回200状态码,验证失败返回401状态码
        if login_user.User == "yinzhengjie" && login_user.Passwd == "123" {
            context.JSON(http.StatusOK, gin.H{
                "Status": "Login successful\n",
            })
        } else {
            context.JSON(http.StatusUnauthorized, gin.H{
                "Status": "Login failed\n",
            })
        }
    })

    //启动路由并指定监听的地址及端口,若不指定默认监听0.0.0.0:8080
    router.Run("172.30.100.101:9000")

    /**
    使用curl命令进行测试:
        [root@yinzhengjie.com ~]# curl -X POST   http://172.30.100.101:9000/login   -H 'content-type: application/json'   -d '{ "user": "yinzhengjie","passwd":"123"}'

    */
}
案例代码

 

三.response及中间件

1>.什么是Context

  Context作为一个数据结构在中间件中传递本次请求的各种数据、管理流程,进行响应在请求来到服务器后,Context对象会生成用来串流程。

2>.响应(response)周期

  整个响应(response)周期:
    (1)路由,找到处理函数(handle)     (2)将请求和响应用Context包装起来供业务代码使用     (3)依次调用中间件和处理函数     (4)输出结果   因为golang原生为web而生而提供了完善的功能,用户需要关注的东西大多数是业务逻辑本身了。
  gin能做的事情也是去把ServeHTTP(ResponseWriter,
*Request)做得高效、友好。一个请求来到服务器了,ServeHTTP会被调用。

3>.设置返回数据

 

4>.自定义中间件

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

/**
自定义一个中间件功能:
    返回的包头(header)信息有咱们自定义的包头信息。
*/
func ResponseHeaders() gin.HandlerFunc {
    return func(context *gin.Context) {
        //自定义包头信息
        context.Header("Access-Control-Allow-Origin", "*")
        context.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CRSF-Token,Authorization,Token")
        context.Header("Access-Control-Allow-Methods", "POST,GET,DELETE,OPTIONS")
        context.Header("Access-Control-Expose-Headers", "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type")
        context.Header("Access-Control-Allow-Credentials", "true")
        //使用"context.Next()"表示继续调用其它的内置中间件,也可以立即终止调用其它的中间件使用"context.Abort()"
        context.Next()

    }
}

func main() {
    /**
    所有的接口都要由路由来进行管理。
        Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求
        同时还有一个Any函数,可以同时支持以上的所有请求。

    创建路由(router)并引入默认中间件
        在源码中,首先是New一个engine,紧接着通过Use方法传入了Logger()和Recovery()这两个中间件。
        其中 Logger 是对日志进行记录,而 Recovery 是对有 painc时, 进行500的错误处理。

    创建路由(router)无中间件
        router := gin.New()
    */
    router := gin.Default()

    //绑定咱们自己定义的中间件
    router.Use(ResponseHeaders())

    router.GET("/middle", func(context *gin.Context) {
        context.String(http.StatusOK, "Response OK\n")
    })

    //启动路由并指定监听的地址及端口,若不指定默认监听0.0.0.0:8080
    router.Run("172.30.100.101:9000")

    /**
    使用curl命令测试:
        [root@yinzhengjie.com ~]# curl -v   http://172.30.100.101:9000/middle
    */
}
案例代码

5>.自定义日志中间件

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "io"
    "net/http"
    "os"
    "time"
)

func main() {
    /**
    所有的接口都要由路由来进行管理。
        Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求
        同时还有一个Any函数,可以同时支持以上的所有请求。

    创建路由(router)并引入默认中间件
        router := gin.Default()
        在源码中,首先是New一个engine,紧接着通过Use方法传入了Logger()和Recovery()这两个中间件。
        其中 Logger 是对日志进行记录,而 Recovery 是对有 painc时, 进行500的错误处理。

    创建路由(router)无中间件
        router := gin.New()
    */
    router := gin.New()

    //创建一个日志文件
    f, _ := os.Create("gin.log")

    //默认数据写入到终端控制台(os.Stdout),我们需要将日志写到咱们刚刚创建的日志文件中
    gin.DefaultWriter = io.MultiWriter(f)

    //自定义你的日志格式
    logger := func(params gin.LogFormatterParams) string {
        return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
            //客户端IP
            params.ClientIP,
            //请求时间
            params.TimeStamp.Format(time.RFC1123),
            //请求方法
            params.Method,
            //请求路径
            params.Path,
            //请求协议
            params.Request.Proto,
            //请求的状态码
            params.StatusCode,
            //请求延迟(耗时)
            params.Latency,
            //请求的客户端类型
            params.Request.UserAgent(),
            //请求的错误信息
            params.ErrorMessage,
        )
    }

    //LoggerWithFormatter 中间件会将日志写入 gin.DefaultWriter
    router.Use(gin.LoggerWithFormatter(logger))

    router.GET("/log", func(context *gin.Context) {
        context.String(http.StatusOK, "自定义日志中间件\n")
    })

    //启动路由并指定监听的地址及端口,若不指定默认监听0.0.0.0:8080
    router.Run("172.30.100.101:9000")

}
案例代码

[GIN-debug] GET    /log                      --> main.main.func2 (2 handlers)
[GIN-debug] Listening and serving HTTP on 172.30.100.101:9000
172.30.100.101 - [Fri, 15 May 2020 06:23:42 CST] "GET /log HTTP/1.1 200 0s "curl/7.29.0" "
172.30.100.101 - [Fri, 15 May 2020 06:23:43 CST] "GET /log HTTP/1.1 200 0s "curl/7.29.0" "
172.30.100.101 - [Fri, 15 May 2020 06:23:46 CST] "GET /log HTTP/1.1 200 0s "curl/7.29.0" "
172.30.100.101 - [Fri, 15 May 2020 06:23:47 CST] "GET /log HTTP/1.1 200 0s "curl/7.29.0" "
172.30.100.101 - [Fri, 15 May 2020 06:23:48 CST] "GET /log HTTP/1.1 200 0s "curl/7.29.0" "
gin.log

 

posted @ 2020-05-11 23:25  JasonYin2020  阅读(5505)  评论(0编辑  收藏  举报