go语言web框架之gin用法

Gin是一个用Go语言编写的web框架,由于使用了httprouter,速度提高了近40倍,通过go doc gin可查看具体哟用法

1、下载并安装Gin,命令如下:

go get -u github.com/gin-gonic/gin

2、下面是一个简单的gin例子,如图:

  • 第10行:初始化一个gin引擎,除了Default外还可以通过gin.New(),二者区别:gin.Default()里面调用了gin.New();在调用完gin.New()得到Engine 实例后,还调用了engine.Use(Logger(), Recovery());gin.Default()获取到的Engine 实例集成了Logger 和 Recovery 中间件
  • 第11行:定义路由,方法为GET,第一个参数为路由的路径,第二个参数为函数,详解见下方
  • 第14行:JSON为gin.Context中的方法,用于返回JSON数据格式,语法为: func (c *Context) JSON(code int, obj any),第一个参数表示返回代码,后面的any表示接收任意参数,any就是一个空接口,在any.go中定义的,gin.H为一个map类型,调用c.JSON主要返回第二各参数any
  • 第20行:启动引擎执行监控,如果不设置端口,默认端口为8080

启动go文件,如图:

通过Postman请求结果如下:

详细实现说明:

(1)、首先查看gin.Default()的返回类型(gin.New()一样),可以看到返回为*Engine,如图:

(2)、查看结构体Engine,可以看到其中嵌套了路由组RouterGroup结构体,如图:

(3)、查看路由组RouterGroup结构体的方法如下:

从上图看出,路由组中包含了我们访问的GET、POST等方法,因此实际请求的时候router.GET实际就是请求的路由组RouterGroup中的方法,方法包含两个参数,第一个参数为字符串,表示路径,第二个参数HandlerFunc其实是一个不定参数的函数(可以接受任意数量的参数)

(4)、查看参数HandlerFunc的类型,如图:

从上图看出,HandlerFunc类型其实是参数为gin.Context的函数,因此在此函数中就可以执行gin.Context中的各类方法了,比如JSON、Bind、ShouldBind等,具体用法通过go doc gin Context查询

3、gin中的FormFile方法用于获取上传文件的信息,参考链接如下:

https://blog.ywdevops.cn/index.php/2022/12/16/upload/

4、gin中的ShouldBindJSON用来绑定传递的json数据,如图:

通过postman请求,如图:

注:在使用ShouldBindJSON时,结构体标签form可写可不写,在使用postman请求的时候,password字段后不能有逗号,否则不符合json格式,会报错的

5、通过Bind()方法绑定数据

下面例子表示通过Bind()方法将数据绑定到结构体test上,Username为必选字段,Password为可选,ctx.JSON中的第一个参数设置为http.StatusForbidden,表示状态码为403,如图:

通过postman调用的时候,将password类型输入为字符串,如图:

可看到postman返回的状态码为400,也就是说如果Bind()绑定失败会在返回400状态码

6、通过ShouldBind()方法绑定数据

修改上图中的方法,将Bind()改为ShouldBind(),如图:

再次执行postman,可以看到状态码为我们自定义的403,如图:

7、Param()方法用于从HTTP请求的URL路径中获取参数值,语法如下:

func (c *Context) Param(key string) string

下面例子表示通过动态路由来下载机器上的文件,如下:

package main

import (
        "fmt"
        "net/http"
        "os"

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

func main() {
        router := gin.New()
        //定义动态路由
        router.GET("/getFile/:filename", func(c *gin.Context) {
                f := c.Param("filename")   //获取参数值
                filePath := "/tmp/" + f
                _, err := os.Stat(filePath)
                if os.IsNotExist(err) {     //判断文件是否存在
                        c.String(http.StatusNotFound, "file not found")
                        return
                }
                c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", f))
                c.Header("Content-Description", "File Transfer")
                c.Header("Content-Type", "application/octet-stream")
                c.File(filePath)  //将文件发送给客户端

        })
        router.Run()
}

通过浏览器访问http://ip/getFile/a.txt即可实现下载a.txt文件

8、通过QueryArray()来获取参数并返回字符串切片,语法如下:

func (c *Context) QueryArray(key string) (values []string)

下面例子通过GET方法,传递参数并通过QueryArray来获取参数,如图:

9、通过PostForm()获取POST请求中从body中传递过来的数据,语法如下:

func (c *Context) PostForm(key string) (value string)

下面例子定义POST请求,通过Body方式传递参数,如图:

通过postman请求,如图:

注意:通过Body方式来传递参数要选择x-www-form-urlencoded,x-www-form-urlencoded适用于传输简单的键值对数据,还有一种是通过url传递参数,此时就要选择Params了

10、通过BindJSON()绑定POST请求中传递的JSON数据

下面例子中发送POST请求,传递JSON数据,并绑定到结构体中,如图:

通过postman请求,选择raw,然后定义JSON数据,如图:

raw是一种直接发送原始数据的方式,可以是纯文本、JSON、XML 等格式。数据以原始的形式发送,没有特定的编码格式

注:BindJSON()和ShouldBindJSON()都用来绑定JSON数据,但是BindJSON()会将错误放在header中,而ShouldBindJSON()则直接返回错误

11、通过ShouldBindWith()来绑定数据

ShouldBindWith()方法是ShouldBind、ShouldBindJSON、ShouldBindXML等这些绑定方法的底层方法,如图:

查看ShouldBindWith()方法的语法如下:

ShouldBindWith的第二个参数是Binding接口,这意味着实现了这个接口方法的结构体都可以作为参数传入

查看Binding接口,位于gin库下的binding包里面,如下:

Binding接口的Bind方法,就是实现参数绑定的核心,目前有十二种绑定引擎可用,如下:

上图中的12个结构体都实现了接口Binding ,因此可直接作为参数传递给Binding,例如上图中的ShouldBindJSON()方法,直接调用的ShouldBindWith的时候直接指定的binding.JSON,因此直接调用jsonBinding结构体中的Bind()方法进行绑定,其余的都是一样,但是ShouldBind()除外,因此这个方法中还有一个Default()函数(如上图),在binding.go中定义,查看Default()函数的语法如下:

从上图看出,如果请求的方法为GET,那么直接返回Form,也就是表单绑定,如果不是GET,也就是POST,此时就要判断contentType了,根据客户端传递的contentType不同,返回不同类型,MIMEJSON表示常量,已经在binding.go中定义,如图:

从上图的switch-case中看到,如果没有指定任何常量,那么默认情况下返回Form,也就是表单绑定,因此ShouldBind()默认情况下实现的就是表单绑定

下面是一个使用ShouldBindWith()的例子,主要功能是通过POST发送请求的时候,传递参数同时还上传图片,如下:

package main

import (
        "log"
        "net/http"

        "github.com/gin-gonic/gin"
        "github.com/gin-gonic/gin/binding"
)

type username struct {
        Username string `form:"username" valid:"Required"`
}

func main() {
        router := gin.New()
        router.POST("/upload", func(c *gin.Context) {
                var form username
                err := c.ShouldBindWith(&form, binding.Form)
                if err != nil {
                        log.Fatal(err)
                }
                m, e := c.FormFile("image")
                if e != nil {
                        log.Fatal(e)
                }

                c.JSON(http.StatusOK, gin.H{
                        "code":     1000,
                        "msg":      "ok",
                        "data":     form.Username,
                        "filename": m.Filename,
                })
        })
        router.Run()
}
  • ShoueldBindWith中指定了binding.Form

上面例子为什么不用ShouldBind而是直接使用ShouldBindWith呢?

因为在 Gin 框架中,一旦使用了 FormFile 方法获取上传的文件,Gin 会自动将请求的 Content-Type 设置为 multipart/form-data,这会导致 ShouldBind 方法无法正确解析请求的参数,为了解决这个问题,可以使用 ShouldBindWith 方法并指定 form 绑定标签来手动解析参数。这样可以绕过 ShouldBind 方法对 Content-Type 的检查

注:Content-Type 字段是用来告诉服务端接收到的数据的类型,它是由客户端在发送请求时设置的。虽然该字段在请求头中,但它是为了服务端而存在的,用于指示服务端如何解析请求体中的数据,客户端在发送请求时通过设置 Content-Type 字段来告知服务端请求体中包含的数据的类型。服务端根据该字段的值来解析请求体中的数据,并做出相应的处理。例如,如果 Content-Type 是 application/json,服务端就会将请求体中的数据解析为 JSON 格式,服务端根据 Content-Type 字段来确定如何处理请求体中的数据,以确保正确解析和处理请求。因此,Content-Type 字段是给服务端看的,用于指示服务端如何处理请求体中的数据类型。客户端设置正确的 Content-Type 字段可以帮助服务端正确地处理请求,并提供正确的响应

标签