gin的url查詢參數解析


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,都視為參數錯誤,對於字符串參數來說,是判斷是否為空字符串。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM