• Gin 笔记(04)— URLQuery参数处理、Bind URI、Bind POST 到指定结构体、POST 表单处理


    1. URLQuery参数处理

    Gin 框架中下列方法可以用处理 URLQuery 参数:

        // 返回指定名字参数的值,c.Params.ByName(key) 简写,
        // 如: "/user/:id",则返回 id := c.Param("id")  id == "john"
        func (c *Context) Param(key string) string
    
        // Query 返回 query 中指定参数的值,如不存在则返回""。
        // c.Request.URL.Query().Get(key) 的简写,
        // 如 GET /path?id=1234&name=Manu&value=,则 c.Query("id") == "1234"
        func (c *Context) Query(key string) string
    
        // DefaultQuery 返回 query 中指定参数的值,如不存在则返回指定的值 defaultValue。
        // GET /?name=Manu&lastname=
        // c.DefaultQuery("name", "unknown") == "Manu"
        // c.DefaultQuery("id", "none") == "none"
        // c.DefaultQuery("lastname", "none") == ""
        func (c *Context) DefaultQuery(key, defaultValue string) string
    
        // GetQuery 类似 Query() , 返回 query 中指定参数的值,如参数存在(即使值为"")
        // 则返回 (value, true),不存在的参数则返回指定的值 ("", false)。
        // c.Request.URL.Query().Get(key) 的简写
        //     GET /?name=Manu&lastname=
        //     ("Manu", true) == c.GetQuery("name")
        //     ("", false) == c.GetQuery("id")
        //     ("", true) == c.GetQuery("lastname")
        func (c *Context) GetQuery(key string) (string, bool)
    
        // 返回 URL 指定名字参数的字符串切片,切片的长度与指定参数的值多少有关
        func (c *Context) QueryArray(key string) []string
    
        //  返回 URL 指定名字参数的字符串切片与布尔值,值存在则为 true
        func (c *Context) GetQueryArray(key string) ([]string, bool)
    
        // 返回 URL 指定名字参数的字符串字典
        func (c *Context) QueryMap(key string) map[string]string
    
        // 返回 URL 指定名字参数的字符串字典与布尔值,值存在则为 true
        func (c *Context) GetQueryMap(key string) (map[string]string, bool)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    1.1 Query 查询字符串参数

    对于类似 /welcome?firstname=Jane&lastname=Doe 这样的 URL? 后面为 Query 查询字符串参数,在 Gin 框架中有专门方法来处理这些参数,例如:

        func main() {
            router := gin.Default()
    
            // 使用现有的基础请求对象解析查询字符串参数。
            // 示例 URL: /welcome?firstname=Jane&lastname=Doe
            router.GET("/welcome", func(c *gin.Context) {
                 firstname := c.DefaultQuery("firstname", "Guest")
                lastname := c.Query("lastname") // c.Request.URL.Query().Get("lastname") 的快捷方式
                name, _ := c.GetQuery("lastname")
    
                c.String(http.StatusOK, "Hello %s %s %s", firstname, lastname, name)
            })
            router.Run(":8080")
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    程序运行在 Debug 模式时,通过浏览器访问

    http://localhost:8080/welcome?firstname=Jane&lastname=Doe
    
    • 1

    上面是通过 Query 方式传递参数,在 Gin 框架中可以通过 Query()DefaultQuery()GetQuery() 等方法得到指定参数的值。

    Query() 读取 URL 中的地址参数,例如

     // GET /path?id=1234&name=Manu&value=
       c.Query("id") == "1234"
       c.Query("name") == "Manu"
       c.Query("value") == ""
       c.Query("wtf") == ""
    
    • 1
    • 2
    • 3
    • 4
    • 5

    DefaultQuery():类似 Query(),但是如果 key 不存在,会返回默认值

     //GET /?name=Manu&lastname=
     c.DefaultQuery("name", "unknown") == "Manu"
     c.DefaultQuery("id", "none") == "none"
     c.DefaultQuery("lastname", "none") == ""
    
    • 1
    • 2
    • 3
    • 4

    输出结果:

    $ curl -X GET  http://localhost:8080/welcome?firstname=wohu\&lastname='1104'
    Hello wohu 1104
    
    $ curl -X GET  "http://localhost:8080/welcome?firstname=wohu&lastname=1104"
    Hello wohu 1104
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.2 URI 路由参数

    对于类似 /user/:firstname/:lastname:lastnameGin 框架中路由参数的一种写法,表示 lastname 为任意的字符串,访问时使用具体值。

    func main() {
        router := gin.Default()
    
        router.GET("/user/:firstname/:lastname", func(c *gin.Context) {
            fname := c.Param("firstname")
            lname := c.Param("lastname")
    
            c.String(http.StatusOK, "Hello %s %s ", fname, lname)
        })
    
        router.Run(":8080")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Param() 方法能快速返回路由 URI 指定名字参数的值,它是 c.Params.ByName(key) 方法的简写。如路由定义为: "/user/:id",则返回 id := c.Param("id")

    程序运行在 Debug 模式时,通过浏览器访问

    http://localhost:8080/user/wohu/1104
    
    • 1

    输出:

    hello wohu 1104
    
    • 1
    func main() {
    	router := gin.Default()
    
    	// This handler will match /user/john but will not match /user/ or /user
    	router.GET("/user/:name", func(c *gin.Context) {
    		name := c.Param("name")	// name == "john"
    		c.String(http.StatusOK, "Hello %s", name)
    	})
    
    	// However, this one will match /user/john/ and also /user/john/send
    	// If no other routers match /user/john, it will redirect to /user/john/
    	router.GET("/user/:name/*action", func(c *gin.Context) {
    		name := c.Param("name")
    		action := c.Param("action")
    		message := name + " is " + action
    		c.String(http.StatusOK, message)
    	})
    
    	router.Run(":8080")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    上面代码路由路径中带参数的方式有 :*两种,不同符号代表不同含义,通过 Param() 方法取得对应的字符串值。

    • :表示参数值不为空,且不以 /结尾;
    • *表示参数可为空,可为任意字符包括 /

    Param() 方法能快速返回路由 URI 指定名字参数的值,它是 c.Params.ByName(key) 方法的简写。如路由定义为: “/user/:id”,则返回 id := c.Param("id")

    1.3 URL Query 字符串参数或表单参数映射到字典

    Gin 框架中 PostFormMap()QueryMap() 等方法在某些情况下非常有用,下面对参数映射到字典做了简单说明,

    func main() {
    	router := gin.Default()
    
    	router.POST("/post", func(c *gin.Context) {
    
    		ids := c.QueryMap("ids")
    		names := c.PostFormMap("names")
    
    		fmt.Printf("ids: %v; names: %v", ids, names)
    	})
    	router.Run(":8080")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    查询

    POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    names[first]=thinkerou&names[second]=tianou
    
    • 1
    • 2
    • 3
    • 4
        curl -H "Content-Type:application/x-www-form-urlencoded" -X POST -d "names[first]=thinkerou&names[second]=tianou" -g "http://localhost:8080/post?ids[a]=1234&ids[b]=hello"
    
    • 1

    输出:

    ids: map[b:hello a:1234], names: map[second:tianou first:thinkerou]
    
    • 1

    2. Binding 绑定

    Bind():将消息体作为指定的格式解析到 Go struct 变量中。而绑定(Binding)是通过一系列方法可以将请求体中参数自动绑定到自定义的结构体中,从而可以简单快速地得到对应的参数值。

    2.1 绑定 URI 到结构体

    package main
    
    import "github.com/gin-gonic/gin"
    
    type Person struct {
    	ID   string `uri:"id" binding:"required,uuid"`
    	Name string `uri:"name" binding:"required"`
    }
    
    func main() {
    	r := gin.Default()
    	r.GET("/:name/:id", func(c *gin.Context) {
    		var person Person
    		if err := c.ShouldBindUri(&person); err != nil {
    			c.JSON(400, gin.H{"msg": err})
    			return
    		}
    		c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
    	})
    	r.Run(":8080")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    gin.H 可当做为字典类型,在 utils.go 文件中定义如下:

    type H map[string]interface{}
    
    • 1

    输出结果:

    $ curl localhost:8080/wohu/987fbc97-4bed-5078-9f07-9141ba07c9f3
    {"name":"wohu","uuid":"987fbc97-4bed-5078-9f07-9141ba07c9f3"}
    
    
    $ curl localhost:8080/wohu/uuid
    {"msg":[{}]}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2 只绑定查询字符串

    type Person struct {
    	Name    string `form:"name"`
    	Address string `form:"address"`
    }
    
    func main() {
    	route := gin.Default()
    	route.Any("/testing", startPage)
    	route.Run(":8085")
    }
    
    func startPage(c *gin.Context) {
    	var person Person
    	if c.ShouldBindQuery(&person) == nil {
    		log.Println("====== Only Bind By Query String ======")
    		log.Println(person.Name)
    		log.Println(person.Address)
    	}
    	c.String(200, "Success")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.3 自定义结构体绑定表单数据请求

    package main
    
    import (
    	"github.com/gin-gonic/gin"
    )
    
    type StructA struct {
    	FieldA string `form:"field_a"`
    }
    
    type StructB struct {
    	StructAValue StructA
    	FieldB       string `form:"field_b"`
    }
    
    type StructC struct {
    	StructAPointer *StructA
    	FieldC         string `form:"field_c"`
    }
    
    type StructD struct {
    	AnonyStruct struct {
    		FieldX string `form:"field_x"`
    	}
    	FieldD string `form:"field_d"`
    }
    
    func GetDataB(c *gin.Context) {
    	var b StructB
    	c.Bind(&b)
    	c.JSON(200, gin.H{
    		"a": b.StructAValue,
    		"b": b.FieldB,
    	})
    }
    
    func GetDataC(c *gin.Context) {
    	var cStruct StructC
    	c.Bind(&cStruct)
    	c.JSON(200, gin.H{
    		"a": cStruct.StructAPointer,
    		"c": cStruct.FieldC,
    	})
    }
    
    func GetDataD(c *gin.Context) {
    	var d StructD
    	c.Bind(&d)
    	c.JSON(200, gin.H{
    		"x": d.AnonyStruct,
    		"d": d.FieldD,
    	})
    }
    
    func main() {
    	r := gin.Default()
    	r.GET("/getb", GetDataB)
    	r.GET("/getc", GetDataC)
    	r.GET("/getd", GetDataD)
    
    	r.Run()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    输出结果:

    $ 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"}
    
    $ curl "http://localhost:8080/getd?field_x=hello&field_d=world"
    {"d":"world","x":{"FieldX":"hello"}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.4 绑定查询字符串或POST数据

    package main
    
    import (
    	"log"
    	"time"
    
    	"github.com/gin-gonic/gin"
    )
    
    type Person struct {
    	Name     string    `form:"name"`
    	Address  string    `form:"address"`
    	Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
    }
    
    func main() {
    	r := gin.Default()
    	r.GET("/testing", startPage)
    	r.Run()
    }
    
    func startPage(c *gin.Context) {
    	var person Person
    	// If `GET`, only `Form` binding engine (`query`) used.
    	// If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
    	// See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48
    	if c.ShouldBind(&person) == nil {
    		log.Println(person.Name)
    		log.Println(person.Address)
    		log.Println(person.Birthday)
    	}
    
    	c.String(200, "Success")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    输出结果:

    $ curl -X GET "localhost:8080/testing?name=wohu&address=city&birthday=1992-03-15"
    Success
    
    • 1
    • 2

    2.5 绑定请求消息体到不同结构体

    绑定请求体的使用方法 c.Request.Body,但是它不能被多次调用。

    type formA struct {
      Foo string `json:"foo" xml:"foo" binding:"required"`
    }
    
    type formB struct {
      Bar string `json:"bar" xml:"bar" binding:"required"`
    }
    
    func SomeHandler(c *gin.Context) {
      objA := formA{}
      objB := formB{}
      // This c.ShouldBind consumes c.Request.Body and it cannot be reused.
      if errA := c.ShouldBind(&objA); errA == nil {
        c.String(http.StatusOK, `the body should be formA`)
      // Always an error is occurred by this because c.Request.Body is EOF now.
      } else if errB := c.ShouldBind(&objB); errB == nil {
        c.String(http.StatusOK, `the body should be formB`)
      } else {
        ...
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    应该使用 c.ShouldBindBodyWith 避免该错误

    func SomeHandler(c *gin.Context) {
      objA := formA{}
      objB := formB{}
      // This reads c.Request.Body and stores the result into the context.
      if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
        c.String(http.StatusOK, `the body should be formA`)
      // At this time, it reuses body stored in the context.
      } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
        c.String(http.StatusOK, `the body should be formB JSON`)
      // And it can accepts other formats
      } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
        c.String(http.StatusOK, `the body should be formB XML`)
      } else {
        ...
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • c.ShouldBindBodyWith在绑定前将 body存储到上下文中。这对性能有轻微的影响,所以如果你足以一次性调用绑定,你不应该使用这个方法。
    • 这个功能只需要用于某些格式  JSON, XML, MsgPack, ProtoBuf。对于其他格式,QueryFormFormPostFormMultipart,可以通过 c.ShouldBind()多次调用而不会对性能造成任何损害。

    2.6 Multipart Urlencoded 绑定

    type LoginForm struct {
    	User     string `form:"user" binding:"required"`
    	Password string `form:"password" binding:"required"`
    }
    
    func main() {
    	router := gin.Default()
    	router.POST("/login", func(c *gin.Context) {
    		// you can bind multipart form with explicit binding declaration:
    		// c.ShouldBindWith(&form, binding.Form)
    		// or you can simply use autobinding with ShouldBind method:
    		var form LoginForm
    		// in this case proper binding will be automatically selected
    		if c.ShouldBind(&form) == nil {
    			if form.User == "user" && form.Password == "password" {
    				c.JSON(200, gin.H{"status": "you are logged in"})
    			} else {
    				c.JSON(401, gin.H{"status": "unauthorized"})
    			}
    		}
    	})
    	router.Run(":8080")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    输出结果:

    $ curl --form user=user --form password=password http://localhost:8080/login
    {"status":"you are logged in"}
    
    • 1
    • 2

    2.7 Multipart/Urlencoded 表单

    表单提交方法为 POST时,enctype 属性为 application/x-www-form-urlencodedmultipart/form-data 的差异:

    func main() {
    	router := gin.Default()
    
    	router.POST("/form_post", func(c *gin.Context) {
    		message := c.PostForm("message")
    		nick := c.DefaultPostForm("nick", "anonymous")
    
    		c.JSON(200, gin.H{
    			"status":  "posted",
    			"message": message,
    			"nick":    nick,
    		})
    	})
    	router.Run(":8080")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    输出结果:
    可以看到在简单的键值对传递时,属性为 application/x-www-form-urlencodedmultipart/form-data 基本不存在差异。都能正常返回 JSON

    curl -H "Content-Type:multipart/form-data" -X POST -d "nick=manu&message=this_is_great" "http://localhost:8080/form_post"
    
    curl -H "Content-Type:application/x-www-form-urlencoded" -X POST -d "nick=manu&message=this_is_great" "http://localhost:8080/form_post"
    
    $ curl -X POST   --form message=message --form nick=nick http://localhost:8080/form_post 
    {"message":"message","nick":"nick","status":"posted"}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3. POST 表单处理

    中,enctype 属性规定当表单数据提交到服务器时如何编码(仅适用于 method="post" 的表单)。formenctype 属性是 HTML5 中的新属性,formenctype 属性覆盖 元素的 enctype 属性。

    常用有两种:application/x-www-form-urlencodedmultipart/form-data,默认为 application/x-www-form-urlencoded

    当表单提交方法为 GET 时,浏览器用 x-www-form-urlencoded 的编码方式把表单数据转换成一个字串(name1=value1&name2=value2...),然后把这个字串追加到 URL 后面。

    当表单提交方法为 POST 时,浏览器把表单数据封装到请求体中,然后发送到服务端。如果此时 enctype 属性为 application/x-www-form-urlencoded,则请求体是简单的键值对连接,格式如下:k1=v1&k2=v2&k3=v3。而如果此时 enctype 属性为 multipart/form-data,则请求体则是添加了分隔符、参数描述信息等内容。

    enctype 属性表

    属性值说明
    application/x-www-form-urlencoded数据被编码为名称/值对,这是默认的编码格式
    multipart/form-data数据被编码为一条消息,每个控件对应消息中的一个部分
    text/plain数据以纯文本形式进行编码,其中不含任何控件或格式字符

    Gin 框架中下列方法可以用处理表单数据:

        // PostForm 从特定的 urlencoded 表单或 multipart 表单返回特定参数的值,不存在则为空""
        func (c *Context) PostForm(key string) string
    
        // DefaultPostForm 从特定的 urlencoded 表单或 multipart 表单返回特定参数的值,
        // 不存在则返回指定的值
        func (c *Context) DefaultPostForm(key, defaultValue string) string
    
        // GetPostForm 类似 PostForm(key).从特定的 urlencoded 表单或 multipart 表单返回特定参数的值,
        // 如参数存在(即使值为"")则返回 (value, true),不存在的参数则返回指定的值 ("", false)。
        // 例如:
        //   email=mail@example.com  -->  ("mail@example.com", true) := GetPostForm("email")
         //  email 为 "mail@example.com"
        //   email=                  -->  ("", true) := GetPostForm("email") // email 值为 ""
        //                           -->  ("", false) := GetPostForm("email") // email 不存在
        func (c *Context) GetPostForm(key string) (string, bool)
    
        // 从特定的 urlencoded 表单或 multipart 表单返回特定参数的字符串切片,
        // 切片的长度与指定参数的值多少有关
        func (c *Context) PostFormArray(key string) []string
    
        //
        func (c *Context) getFormCache()
    
        // 从特定的 urlencoded 表单或 multipart 表单返回特定参数的字符串切片,
        // 至少一个值存在则布尔值为true
        func (c *Context) GetPostFormArray(key string) ([]string, bool)
    
        // 从特定的 urlencoded 表单或 multipart 表单返回特定参数的字符串字典
        func (c *Context) PostFormMap(key string) map[string]string
    
        // 从特定的 urlencoded 表单或 multipart 表单返回特定参数的字符串字典,
        // 至少一个值存在则布尔值为true
        func (c *Context) GetPostFormMap(key string) (map[string]string, bool)
    
        // 返回表单指定参数的第一个文件
        func (c *Context) FormFile(name string) (*multipart.FileHeader, error)
    
        // 分析multipart表单,包括文件上传
        func (c *Context) MultipartForm() (*multipart.Form, error)
    
        // 将表单文件上传到特定dst
        func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    参数和表单混合处理

    func main() {
    	router := gin.Default()
    
    	router.POST("/post", func(c *gin.Context) {
    
    		id := c.Query("id")
    		page := c.DefaultQuery("page", "0")
    		name := c.PostForm("name")
    		message := c.PostForm("message")
    
    		fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
    	})
    	router.Run(":8080")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    下面是请求 Request 的头信息,分为四部分,请求行,请求头,空行,请求体:输入输出

    POST /post?id=1234&page=1 HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    name=manu&message=this_is_great
    
    curl -H "Content-Type:application/x-www-form-urlencoded" -X POST -d "name=manu&message=this_is_great" "http://localhost:8080/post?id=1234&page=1"
    id: 1234; page: 1; name: manu; message: this_is_great
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    参考:
    https://github.com/gin-gonic/gin
    https://github.com/gin-gonic/examples
    https://gin-gonic.com/docs/introduction/

    https://www.jianshu.com/p/a31e4ee25305
    https://blog.csdn.net/u014361775/article/details/80582910
    https://learnku.com/docs/gin-gonic/1.7/examples-ascii-json/11362

  • 相关阅读:
    【监督学习】基于合取子句进化算法(CCEA)和析取范式进化算法(DNFEA)解决分类问题(Matlab代码实现)
    Java项目:SSH学生学籍管理系统及教务管理系统
    电商项目常用的五个设计模式场景及分析实现
    瑞芯微:基于RK3568的深度估计模型部署
    TensorFlow入门(十九、softmax算法处理分类问题)
    JPA 中使用 @OneToMany 、@ManyToOne 等关系映射注解
    如何解决Ubuntu中软件包安装时的404错误(无法安装gdb、cgddb等)
    pandas写入MySQL
    【Proteus仿真】【Arduino单片机】HC05蓝牙通信
    ROS的调试经验
  • 原文地址:https://blog.csdn.net/wohu1104/article/details/126533806