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

 生成的前缀数如下所示:

原文地址:https://www.cnblogs.com/forever521Lee/p/9507378.html