gin作为go语言最知名的网络库,在这里我简要介绍一下url的查询参数解析。主要是这里面存在一些需要注意的地方。这里,直接给出代码,和运行结果,在必要的地方进行分析。

代码1:

type StructA struct {
    FieldA string `form:"field_a"`
}

type StructB struct {
    NestedStruct StructA
    FieldB string `form:"field_b"`
}

type StructC struct {
    NestedStructPointer *StructA
    FieldC string `form:"field_c"`
}

func GetDataB(c *gin.Context) {
    var b StructB
    c.Bind(&b)
    c.JSON(200, gin.H{
        "a": b.NestedStruct,
        "b": b.FieldB,
    })
}

func GetDataC(c *gin.Context) {
    var b StructC
    c.Bind(&b)
    c.JSON(200, gin.H{
        "a": b.NestedStructPointer,
        "c": b.FieldC,
    })
}

func main() {
    r := gin.Default()
    r.GET("/getb", GetDataB)
    r.GET("/getc", GetDataC)

    r.Run()
}

 测试结果:

$ curl "http://localhost:8080/getb?field_a=hello&field_b=world"
{"a":{"FieldA":"hello"},"b":"world"}
$ curl "http://localhost:8080/getc?field_a=hello&field_c=world"
{"a":{"FieldA":"hello"},"c":"world"}

上述结果显示gin的query解析可以嵌套赋值,只需要form tag和传入的参数一致。
再看下面的代码:
代码2:
package main

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

type StructA struct {
	FieldA string `form:"field_a"`
}

type StructB struct {
	StructA
	FieldB string `form:"field_b"`
}

type StructC struct {
	*StructA
	FieldC string `form:"field_c"`
}

func GetDataB(c *gin.Context) {
	var b StructB
	c.Bind(&b)
	c.JSON(200, gin.H{
		"a": b.FieldA,
		"b": b.FieldB,
	})
}

func GetDataC(c *gin.Context) {
	var b StructC
	c.Bind(&b)
	c.JSON(200, gin.H{
		"a": b.FieldA,
		"c": b.FieldC,
	})
}

func main() {
	r := gin.Default()
	r.GET("/getb", GetDataB)
	r.GET("/getc", GetDataC)

	r.Run()
}

 输出结果:

curl "http://localhost:8080/getb?field_a=hello&field_b=world"
{"a":"hello","b":"world"}

curl "http://localhost:8080/getc?field_a=hello&field_c=world"
{"a":"hello","c":"world"}

结果显示,gin的url查询参数解析可以正常处理嵌套的结构体,只需要form tag和传入的参数一致。

再看下面代码:

 代码3:

package main

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

type structA struct {
	FieldA string `form:"field_a"`
}

type StructB struct {
	structA
	FieldB string `form:"field_b"`
}

type StructC struct {
	*structA
	FieldC string `form:"field_c"`
}

func GetDataB(c *gin.Context) {
	var b StructB
	c.Bind(&b)
	c.JSON(200, gin.H{
		"a": b.FieldA,
		"b": b.FieldB,
	})
}

func GetDataC(c *gin.Context) {
	var b StructC
	c.Bind(&b)
	c.JSON(200, gin.H{
		"a": b.FieldA,
		"c": b.FieldC,
	})
}

func main() {
	r := gin.Default()
	r.GET("/getb", GetDataB)
	r.GET("/getc", GetDataC)

	r.Run()
}

 

注意,上述代码只是将StructA改为structA,也就是说大小写变化。测试结果如下:

curl "http://localhost:8080/getb?field_a=hello&field_b=world"
{"a":"","b":"world"}

curl "http://localhost:8080/getc?field_a=hello&field_c=world"

客户端显示为空,服务器打印一个panic语句,错误类型是runtime error: invalid memory address or nil pointer dereference,一系列panic堆栈,然后是:

[GIN] 2019/04/11 - 22:07:01 | 500 |    2.403482ms |       127.0.0.1 | GET      /getc?field_a=hello&field_c=world,显示状态码是500。

可见,对于结构体嵌套解析,只有结构体是大写的情况下才可以有效解析,小写的情况下,要么不解析,要么解析出现异常。

下面再看一段代码,官方提示的错误:

代码4:

package main

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

type StructA struct {
	FieldA string
}

type StructB struct {
	NestedStruct StructA `form:"field_a"`
	FieldB       string  `form:"field_b"`
}

type StructC struct {
	NestedStructPointer *StructA `form:"field_a"`
	FieldC              string   `form:"field_c"`
}

func GetDataB(c *gin.Context) {
	var b StructB
	c.Bind(&b)
	c.JSON(200, gin.H{
		"a": b.NestedStruct,
		"b": b.FieldB,
	})
}

func GetDataC(c *gin.Context) {
	var b StructC
	c.Bind(&b)
	c.JSON(200, gin.H{
		"a": b.NestedStructPointer,
		"c": b.FieldC,
	})
}

func main() {
	r := gin.Default()
	r.GET("/getb", GetDataB)
	r.GET("/getc", GetDataC)

	r.Run()
}

 这里注意StructA结构体的tag的位置发生了变化。结果如下:

curl "http://localhost:8080/getb?field_a=hello&field_b=world"
{"a":{"FieldA":""},"b":""}

curl "http://localhost:8080/getc?field_a=hello&field_c=world"
{"a":null,"c":""}

可见,这种书写tag的方式存在问题,对应的结构体成员不能正确的解析。)

 

关于其他情况, 经过测试

(1)对于代码1,将其中的StructA改写外structA,参数可以正确解析。

(2)对于代码1,将NestedStruct改为nestedStruct, NestedStructPointer改为nestedStructPointer,对应字段不再解析。

 

关于tag中的binding:"required"参数,我有一点需要补充的,下面为实例代码:

代码5:

package main

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

type StructA struct {
	FieldA int `form:"field_a" binding:"required"`
}

type StructB struct {
	NestedStruct StructA
	FieldB       string `form:"field_b" binding:"required"`
}

func GetDataB(c *gin.Context) {
	var b StructB
	c.Bind(&b)
	c.JSON(200, gin.H{
		"a": b.NestedStruct,
		"b": b.FieldB,
	})
}

func main() {
	r := gin.Default()
	r.GET("/getb", GetDataB)

	r.Run()
}

 注意FieldA的类型和tag,类型由String改为int, tag添加了`bind:"required"`。测试结果如下:

curl "http://localhost:8080/getb?field_a=hello&field_b=world"
{"a":{"FieldA":0},"b":""}

服务端有一个额外的打印:

[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 200

curl "http://localhost:8080/getb?field_a=0&field_b=world"
{"a":{"FieldA":0},"b":"world"}

服务端有一个额外的打印:

[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 200

curl "http://localhost:8080/getb?field_b=world"
{"a":{"FieldA":0},"b":"world"}

服务端有一个额外的打印:

[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 200

curl "http://localhost:8080/getb?field_a=1&field_b=world"
{"a":{"FieldA":1},"b":"world"}

服务端没有额外的打印。

curl "http://localhost:8080/getb?field_a=1&field_b="
{"a":{"FieldA":1},"b":""}

服务端有一个额外的打印:

[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 200

可见,gin对于bind:"required"的判断非常简单,对于int类型,是判断这个int类型是否为0,无论是否显示设置的0,都视为参数错误,对于字符串参数来说,是判断是否为空字符串。

 

内容来源于网络如有侵权请私信删除
你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!

相关课程

3512 0元 限免