zoukankan      html  css  js  c++  java
  • Go语言基础之20--web编程框架之Gin框架

    一、Gin框架介绍

    1.1 简介

    A. 基于httprouter开发的web框架。 http://github.com/julienschmidt/httprouter

    B. 提供Martini风格的API,但比Martini要快40倍

    C. 非常轻量级,使用起来非常简洁

    1.2 Gin框架的安装与使用

    A. 安装: go get -u github.com/gin-gonic/gin

    B. import "go get -u github.com/gin-gonic/gin"

    实例1:

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        r := gin.Default()                    //Default返回一个默认的路由引擎
        r.GET("/ping", func(c *gin.Context) { //c这里就是封装了go底层web的r和w
            c.JSON(200, gin.H{   //200是状态码
                "message": "pong",
            }) //输出json结果给调用方
        })
        r.Run() // listen and serve on 0.0.0.0:8080
    }

    执行结果:

    浏览器查看:

    实例2:

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    type Result struct {
        Message string `json:"message"`
        Code    int    `json:"code"`
    }
    
    func handleUserInfo(c *gin.Context) {
    
        var result Result = Result{ //初始化1个map
            Message: "success",
            Code:    0,
        }
    
        c.JSON(http.StatusOK, result)  //第二个参数是一个空接口,可以传递任何类型数据进来,这里传递进来的map会自动把我们转成json串
    }
    
    func main() {
        r := gin.Default()
        r.GET("/user/info", handleUserInfo)
    
        r.Run(":9090")
    }

    执行结果:

    浏览器查看:

    1.3 Restful风格的API 

    A. 把我们设计的API抽象成一个个资源,用URI来标识。

    B. 针对每一个资源,使用统一的方法进行操作。

    C. 同一的方法有:

    a. GET,获取资源内容。

    b. POST,创建资源。

    c. PUT,更新资源。

    d. DELETE,删除资源。

    样例:

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        //Default返回一个默认的路由引擎
        r := gin.Default()
        r.GET("/ping", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.POST("/ping", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.Run() // listen and serve on 0.0.0.0:8080
    }

    1.4 实例1  restful风格的API 用户信息接口设计,资源就是 /user/info

    样例:

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        //Default返回一个默认的路由引擎
        r := gin.Default()
        r.GET("/user/info", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.POST("/user/info", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.PUT("/user/info", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.DELETE("/user/info", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.Run() // listen and serve on 0.0.0.0:8080
    }

    1.5 实例2   用户信息接口设计,非restful风格的API

     样例:

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        //Default返回一个默认的路由引擎
        r := gin.Default()
        r.GET("/user/info", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.POST("/user/create", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.POST("/user/delete", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.POST("/user/update", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "pong",
            })
        })
        r.Run() // listen and serve on 0.0.0.0:8080
    }

    二、Gin框架参数传递

    2.1 通过querystring传递, 比如: /user/search?username=少林&address=北京

    实例1:

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        //Default返回一个默认的路由引擎
        r := gin.Default()
        r.GET("/user/search", func(c *gin.Context) {
            //username := c.DefaultQuery("username", "少林")
            username := c.Query("username")   //通过query来获取用户传递过来的参数
            address := c.Query("address")
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message":  "pong",
                "username": username,
                "address":  address,
            })
        })
        r.Run() // listen and serve on 0.0.0.0:8080
    }

    浏览器端查看:

    实例2:

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    type Result struct {
        Message string `json:"message"`
        Code    int    `json:"code"`
    }
    
    type UserInfo struct {
        Result
        UserName string `json:"username"`
        Passwd   string `json:"passwd"`
    }
    
    func handleUserInfo(c *gin.Context) {
    
        username := c.Query("username")
        passwd := c.DefaultQuery("passwd", "dkdkdkdkdkdkdkd") //默认值,如果用户没有传递过来值,则为此处定义默认值
    
        var result UserInfo = UserInfo{
            UserName: username,
            Passwd:   passwd,
        }
    
        result.Code = 0
        result.Message = "success"
    
        c.JSON(http.StatusOK, result)
    }
    
    func main() {
        r := gin.Default()
        r.GET("/user/info", handleUserInfo)
    
        r.Run(":9090")
    }

    执行结果:

    浏览器端查看:

    用户不传递参数:

    用户传递参数:

    2.2 通过路径传递, 比如: /user/search/少林/北京

    实例1:

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        //Default返回一个默认的路由引擎
        r := gin.Default()
        r.GET("/user/info/:username/:address", func(c *gin.Context) {
            //username := c.DefaultQuery("username", "少林")
            username := c.Param("username") //用param方法
            address := c.Param("address")
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message":  "pong",
                "username": username,
                "address":  address,
            })
        })
        r.Run() // listen and serve on 0.0.0.0:8080
    }

     执行结果:

    浏览器端查看:

    实例2:

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    type Result struct {
        Message string `json:"message"`
        Code    int    `json:"code"`
    }
    
    type UserInfo struct {
        Result
        UserName string `json:"username"`
        Passwd   string `json:"passwd"`
    }
    
    func handleUserParams(c *gin.Context) {
    
        username := c.Param("username")
        passwd := c.Param("passwd")
    
        var result UserInfo = UserInfo{
            UserName: username,
            Passwd:   passwd,
        }
    
        result.Code = 0
        result.Message = "success"
    
        c.JSON(http.StatusOK, result)
    }
    
    func main() {
        r := gin.Default()
        r.GET("/user/info/:username/:passwd", handleUserParams)
    
        r.Run(":9090")
    }

    执行结果:

    浏览器查看:

    2.3 通过表单进行提交, 比如: POST /user/search/

    下载postman测试工具,测试api有很多也非常方便。

    下载地址: https://www.getpostman.com/apps

    代码实例如下:

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    type Result struct {
        Message string `json:"message"`
        Code    int    `json:"code"`
    }
    
    type UserInfo struct {
        Result
        UserName string `json:"username"`
        Passwd   string `json:"passwd"`
    }
    
    func handleUserInfo(c *gin.Context) {
    
        username := c.PostForm("username")   //使用postform函数获取表单数据
        passwd := c.PostForm("passwd")
    
        var result UserInfo = UserInfo{
            UserName: username,
            Passwd:   passwd,
        }
    
        result.Code = 0
        result.Message = "success"
    
        c.JSON(http.StatusOK, result)
    }
    
    func main() {
        r := gin.Default()
        r.POST("/user/info", handleUserInfo)  //选择post方法
    
        r.Run(":9090")
    }

     执行结果:

     

    通过postman测试结果:

    三、Gin框架处理文件上传

    3.1 单个文件上传

    package main
    
    import (
        "fmt"
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    type Result struct {
        Message string `json:"message"`
        Code    int    `json:"code"`
    }
    
    type UserInfo struct {
        Result
        UserName string `json:"username"`
        Passwd   string `json:"passwd"`
    }
    
    func handleUpload(c *gin.Context) {
    
        file, err := c.FormFile("testfile") //通过formfile方法来获取上传的文件,传入的参数是对上传文件的标识(可以随便起名),也就是要上传的文件的key
        if err != nil {
            fmt.Printf("upload file failed")
            return
        }
    
        filename := fmt.Sprintf("C:/tmp/%s", file.Filename) //文件保存位置
        err = c.SaveUploadedFile(file, filename)            //调用c将上传文件保存至指定位置
        if err != nil {
            fmt.Printf("save file failed, err:%v
    ", err)
            return
        }
    
        c.JSON(http.StatusOK, "file upload success")    //http.StatusOK就是返回状态码200
    }
    
    func main() {
        r := gin.Default()
        r.POST("/file/upload", handleUpload)
    
        r.Run(":9090")
    }

     执行结果:

    postman执行结果:

    保存文件查看结果:

    3.2 多个文件上传

    package main
    
    import (
        "fmt"
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    type Result struct {
        Message string `json:"message"`
        Code    int    `json:"code"`
    }
    
    type UserInfo struct {
        Result
        UserName string `json:"username"`
        Passwd   string `json:"passwd"`
    }
    
    func handleMultiUpload(c *gin.Context) {
    
        form, err := c.MultipartForm() //多个文件用multipartform方法
        if err != nil {
            fmt.Printf("upload file failed")
            return
        }
    
        files := form.File["testfile"] //传入文件名的key,其返回值是一个数组
        for _, file := range files {   //遍历数组,即可把要传入的多个文件都保存起来了
            filename := fmt.Sprintf("C:/tmp/%s", file.Filename)
            err = c.SaveUploadedFile(file, filename)
            if err != nil {
                fmt.Printf("save file failed, err:%v
    ", err)
                return
            }
        }
        c.JSON(http.StatusOK, "file upload success")
    }
    
    func main() {
        r := gin.Default()
        r.POST("/files/upload", handleMultiUpload)
    
        r.Run(":9090")
    }

    执行结果: 

    postman执行结果:

    保存文件查看结果:

    四、Gin框架路由分组

    路由分组做的就是把相同的前缀通过借助group方法实现分组。

    实例:

    package main
    
    import "github.com/gin-gonic/gin"
    
    func login(ctx *gin.Context) {
        ctx.JSON(200, gin.H{
            "message": "success",
        })
    }
    func submit(ctx *gin.Context) {
        ctx.JSON(200, gin.H{
            "message": "success",
        })
    }
    func main() {
        //Default返回一个默认的路由引擎
        router := gin.Default()
        // Simple group: v1
        v1 := router.Group("/v1")
        {
            v1.GET("/login", login) // 等价于/v1/login
            v1.POST("/submit", submit)
        }
        // Simple group: v2
        v2 := router.Group("/v2")
        {
            v2.POST("/login", login)
            v2.POST("/submit", submit) //等价于/v2/submit
        }
        router.Run(":8080")
    }

    执行结果:

    验证测试:

    五、Gin框架参数绑定(方便)

    为什么要参数绑定,本质上是方便,提高开发效率

    A. 通过反射的机制,自动提取querystring、 form表单、 json、 xml等参数到struct中

    B. 通过http协议中的context type,识别是json、 xml或者表单

    参考网址:

    http://www.zhimengzhe.com/bianchengjiaocheng/qitabiancheng/28966.html

    注意:

    要绑定一个请求body到某个类型, 可以使用model binding。 目前支持JSON, XML 以及标准from格式 (foo=bar&boo=baz)的绑定。

    所有你想要绑定的域(field), 需要你设置对应的绑定标识。 例如, 要绑定到JSON, 则这样声明json:"fieldname"。

    使用Bind方法时, Gin会尝试通过Content-Type头部来推定绑定的类型(如json还是form)。而如果你明确知道要绑定的类型, 可以使用BindWith方法。

    你也可以指定哪些filed需要绑定。 如果某个filed像这样声明: binding:"required", 那么在进行绑定时如果发现是空值(注: 是请求中不存在该field名?), 当前的请求会失败并收到错误提示。

    实例1:

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    type UserInfo struct {
        UserName string `form:"username" json:"username" binding:"required"` //form表单、json串
        Passwd   string `form:"passwd" json:"passwd" binding:"required"`
        Age      int    `form:"age" json:"age" binding:"required"`
        Sex      string `form:"sex" json:"sex" binding:"required"`
    }
    
    //form
    func handleUserInfoForm(c *gin.Context) {
        var userInfo UserInfo
        err := c.ShouldBind(&userInfo) //直接将结构体的4个参数选项绑定过来,借助ShouldBind
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        } else {
            c.JSON(http.StatusOK, userInfo)
        }
    }
    
    //json
    func handleUserInfoJson(c *gin.Context) {
        var userInfo UserInfo
        err := c.ShouldBindJSON(&userInfo)
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        } else {
            c.JSON(http.StatusOK, userInfo)
        }
    }
    
    //querystring
    func handleUserInfoQuery(c *gin.Context) {
        var userInfo UserInfo
        err := c.ShouldBind(&userInfo)
        if err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        } else {
            c.JSON(http.StatusOK, userInfo)
        }
    }
    
    func main() {
        r := gin.Default()
    
        // /v1/user/login
        // /v1/user/login2gi
        v1Group := r.Group("/v1")
        v1Group.POST("/user/infoform", handleUserInfoForm)
        v1Group.POST("/user/infojson", handleUserInfoJson)
        v1Group.GET("/user/infoquerystring", handleUserInfoQuery)
        r.Run(":9090")
    }

     执行结果:

    测试结果:

    form:

    json:

    Querystring:

    六、Gin框架渲染

    6.1 渲染json

    gin.Context.JSON方法进行渲染

    代码实例:

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        r := gin.Default()
        // gin.H is a shortcut for map[string]interface{}
        r.GET("/someJSON", func(c *gin.Context) {
            //第一种方式,自己拼json
            c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) //gin.H就是一个map的别名,借助gin.H可以去拼接json
        })
    
        //第二种:通过定义一个结构体,然后将结构体传给c.JSON,之后会将传递过来的结构体打包成支持json协议的字符串返回给客户端
        r.GET("/moreJSON", func(c *gin.Context) {
            // You also can use a struct
            var msg struct {
                Name    string `json:"user"`
                Message string
                Number  int
            }
            msg.Name = "Lena"
            msg.Message = "hey"
            msg.Number = 123
            // Note that msg.Name becomes "user" in the JSON
            c.JSON(http.StatusOK, msg)
        })
        // Listen and serve on 0.0.0.0:8080
        r.Run(":8080")
    }

     执行结果:

    浏览器测试:

    6.2 渲染xml

    gin.Context.XML方法进行渲染

    代码实例1:

    package main
    
    import (
        "github.com/gin-gonic/gin"
    )
    
    type Result struct {
        Message string `json:"message"`
        Code    int    `json:"code"`
    }
    
    type UserInfo struct {
        Result
        UserName string `json:"username"`
        Passwd   string `json:"passwd"`
    }
    
    func handleUserInfo(c *gin.Context) {
    
        var userInfo = &UserInfo{
            UserName: "skkss",
            Passwd:   "SSSS",
        }
    
        c.XML(200, userInfo)  //将结构体传递给c.xml
    }
    
    func main() {
        r := gin.Default()
        r.GET("/user/info", handleUserInfo)
    
        r.Run(":9090")
    }

     执行结果:

    浏览器测试:

    实例2:

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        r := gin.Default()
        r.GET("/moreXML", func(c *gin.Context) {
            // You also can use a struct
            type MessageRecord struct {
                Name    string
                Message string
                Number  int
            }
            var msg MessageRecord
            msg.Name = "Lena"
            msg.Message = "hey"
            msg.Number = 123
            c.XML(http.StatusOK, msg)
        })
        // Listen and serve on 0.0.0.0:8080
        r.Run(":8080")
    }

    6.3 渲染模板

    gin.Context.HTML方法进行渲染

    代码实例目录结构:

    posts.html:

    {{ define "post" }}     <!-- 用于生命要传递的名字,传递给go源码,相当于模板的key -->
    <html><h1>
        {{ . }}
    </h1>
    <p>gin.Context.HTML方法进行渲染</p>
    </html>
    {{ end }}
    posts.html

    main.go:

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func handleHtml(c *gin.Context) {
    
        c.HTML(http.StatusOK, "post", "ksdkfskfkskfsdkfs") //post是html文件定义的名字
    }
    
    func main() {
        r := gin.Default()
        r.LoadHTMLGlob("./templates/*") //将模板文件加载进来
    
        r.GET("/user/info", handleHtml)
    
        r.Run(":9090")
    }

     执行结果:

    浏览器测试:

    实例2:

    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        router := gin.Default()
        router.LoadHTMLGlob("templates/**/*") //**代表所有子目录 *代表子目录下的所有模板
        router.GET("/posts/index", func(c *gin.Context) {
            c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
                "title": "Posts",
            })
        })
        router.GET("/users/index", func(c *gin.Context) {
            c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
                "title": "Users",
            })
        })
        router.Run(":8080")
    }

    七、 Gin框架中间件&路由原理

    7.1 Gin框架中间件

    A. Gin框架允许在请求处理过程中,加入用户自己的钩子函数。这个钩子函数就叫中间件

    B. 因此,可以使用中间件处理一些公共业务逻辑,比如耗时统计,日志打印,登陆校验(不可能说每个api都写这些公共逻辑,我们可以将公共逻辑放到中间件中)

    7.2 自己编写中间件

    实例1:

    package main
    
    import (
        "fmt"
        "net/http"
        "time"
    
        "github.com/gin-gonic/gin"
    )
    
    func StatCost(c *gin.Context) {  //统计耗时函数,这就是一个中间件
        start := time.Now()
        fmt.Printf("start stat cost
    ")
        c.Next()  ////等中间件中其他函数先执行(也就是说执行完c.Next其他函数已经执行完了)
        lattancy := time.Since(start)  //获取耗时
        fmt.Printf("process request cost:%d ms
    ", lattancy/1000/1000)
    
    }
    
    func handleUserInfo(c *gin.Context) {  //此请求处理函数会在中间件中进行处理,该函数执行完,才会执行c.Next
        fmt.Printf("request start process
    ")
        time.Sleep(3 * time.Second)
        c.JSON(http.StatusOK, "38333k333")
    }
    
    func main() {
        r := gin.Default()
        r.Use(StatCost)  //将中间件函数设置(借助use函数)到gin框架,这样gin框架在处理请求时,就会先调用中间件进行处理
    
        r.GET("/user/info", handleUserInfo)
        r.Run(":8080")
    }

     执行结果:

    浏览器测试:

    实例2:

    package main
    
    import (
        "log"
        "net/http"
        "time"
    
        "github.com/gin-gonic/gin"
    )
    
    func StatCost() gin.HandlerFunc {
        return func(c *gin.Context) {
            t := time.Now()
            //可以设置一些公共参数
            c.Set("example", "12345")
            //等其他中间件先执行
            c.Next()
            //获取耗时
            latency := time.Since(t)
            log.Print(latency)
        }
    }
    func main() {
        r := gin.New()
        r.Use(StatCost())
        r.GET("/test", func(c *gin.Context) {
            example := c.MustGet("example").(string)
            // it would print: "12345"
            log.Println(example)
            c.JSON(http.StatusOK, gin.H{
                "message": "success",
            })
        })
        // Listen and serve on 0.0.0.0:8080
        r.Run(":8080")
    }

    7.3 Gin框架路由介绍

    A. 路由部分用的是: http://github.com/julienschmidt/httprouter

    B. 对于所有的路由规则, httprouter会构造一颗前缀树

    httprouter官网例子:

    package main
    
    import "github.com/gin-gonic/gin"
    
    func index(ctx *gin.Context) {
        ctx.JSON(200, gin.H{
            "message": "index",
        })
    }
    func main() {
        //Default返回一个默认的路由引擎
        router := gin.Default()
        router.POST("/", index)
        router.POST("/search", index)
        router.POST("/support", index)
        router.POST("/blog/:post", index)
        router.POST("/about", index)
        router.POST("/contact", index)
        router.Run(":8080")
    }

     生成的前缀数如下所示:

  • 相关阅读:
    Android 内存剖析 – 发现潜在问题
    Android内存泄漏问题(一)
    Android的事件处理机制详解(二)-----基于监听的事件处理机制
    Android的事件处理机制(一)------基于回调机制的事件处理
    OOA、OOD 和 OOP
    面向对象初识
    Python 2 和 Python 3 的区别
    软件开发规范
    语法分析和词法分析
    循环删除的坑
  • 原文地址:https://www.cnblogs.com/forever521Lee/p/9507378.html
Copyright © 2011-2022 走看看