• 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
    ", 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
    ", 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
    ",
                })
            } else {
                context.JSON(http.StatusUnauthorized, gin.H{
                    "Status": "Login failed
    ",
                })
            }
        })
    
        //启动路由并指定监听的地址及端口,若不指定默认监听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
    ")
        })
    
        //启动路由并指定监听的地址及端口,若不指定默认监听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"
    ",
                //客户端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, "自定义日志中间件
    ")
        })
    
        //启动路由并指定监听的地址及端口,若不指定默认监听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
  • 相关阅读:
    servlet 与缓存(4)
    向架构师进军---&gt;系统架构设计基础知识
    sql语法:inner join on, left join on, right join on具体用法
    关于sources.list和apt-get [转载]
    SoftReference
    MFC 之 截图工具
    Outlook Express 收发邮件出现&quot;0x800CCC0F&quot;错误代码解决方法
    使用ffmpeg视频编码过程中踩的一个坑
    Libgdx环境搭建及介绍
    从简单的信道预计说起
  • 原文地址:https://www.cnblogs.com/yinzhengjie2020/p/12873039.html
Copyright © 2020-2023  润新知