goframe与gin对比(三) 请求输入

请求输入

复杂参数

同名参数

同名参数提交格式形如:k=v1&k=v2, goframe是后续的变量值将会覆盖前面的变量值,而gin因为用的标准库net/http, 提交的同名参数将会被转换为字符串数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)

func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write(r.Get("name"))
})
s.SetPort(8199)
s.Run()
}

访问http://localhost:8199/?name=john&name=smith, 得到smith

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
"net/http"

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

func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
name := c.Query("name")
nameArray := c.QueryArray("name")
c.String(http.StatusOK, fmt.Sprintf("hello %s ", name))
c.String(http.StatusOK, fmt.Sprintf("hello %s %s", nameArray[0], nameArray[1]))
})
r.Run()
}

访问http://localhost:8080/?array=john&array=smith, 得到hello john hello john smith

数组参数

数组参数提交格式形如:k[]=v1&k[]=v2, goframe 与 gin 相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
"net/http"

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

func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
// http://localhost:8080/?array[]=john&array[]=smith
array := c.QueryArray("array[]")
c.String(http.StatusOK, fmt.Sprintf("hello %s %s", array[0], array[1]))
})
r.Run()
}

访问http://localhost?array[]=john&array[]=smith, 得到["john","smith"]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)

func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write(r.Get("array"))
})
s.Run()
}

访问http://localhost:8080/?array[]=john&array[]=smith, 得到hello john smith

对象处理

可将数据解析与绑定分为 json, xml, 表单, uri 等几种。goframe和gin均支持这几种数据的绑定与解析。

goframe推荐使用 parse转换来实现struct的转换。可参考goframe请求输入-对象处理文档

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
package main

import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)

type RegisterReq struct {
Name string
Pass string `p:"password1"`
Pass2 string `p:"password2"`
}

type RegisterRes struct {
Code int `json:"code"`
Error string `json:"error"`
Data interface{} `json:"data"`
}

func main() {
s := g.Server()
s.BindHandler("/register", func(r *ghttp.Request) {
var req *RegisterReq
if err := r.Parse(&req); err != nil {
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: err.Error(),
})
}
// ...
r.Response.WriteJsonExit(RegisterRes{
Data: req,
})
})
s.SetPort(8199)
s.Run()
}

访问http://127.0.0.1:8199/register?name=john&password1=123&password2=456, 得到{"code":0,"error":"","data":{"Name":"john","Pass":"123","Pass2":"456"}}

gin 和 goframe 类似

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
package main

import (
"net/http"

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

type RegisterReq struct {
Name string
Pass string `form:"password1" binding:"required"`
Pass2 string `form:"password2" binding:"required"`
}

type RegisterRes struct {
Code int `json:"code"`
Error string `json:"error"`
Data interface{} `json:"data"`
}

func main() {
r := gin.Default()
r.GET("/register", func(c *gin.Context) {

var req RegisterReq
if err := c.ShouldBind(&req); err != nil {
c.JSON(http.StatusBadRequest, RegisterRes{
Code: 1,
Error: err.Error(),
Data: req})
return
}

c.JSON(http.StatusOK, RegisterRes{Data: req})
})
r.Run()
}

访问http://localhost:8080/register?name=john&password1=pwd1&password2=pwd2,可得到{"code":0,"error":"","data":{"Name":"","Pass":"pwd1","Pass2":"pwd2"}}

展示的案例是绑定get请求的query参数,其他的情况可参考

只绑定Get参数 官方
只绑定Get参数 中文
绑定Get参数或者Post参数 官方
绑定Get参数或者Post参数 中文
绑定uri 官方
绑定uri 中文
绑定HTML复选框 官方
绑定HTML复选框 中文
绑定Post参数 官方
绑定Post参数 中文
表单数据解析和绑定
URI数据解析和绑定

JSON/XML

从GoFrame v1.11版本开始,Request对象提供了对客户端提交的JSON/XML数据格式的原生支持。可通过 Parse 方法转换json或xml

具体案例可参考 GoFrame 请求输入JSON/XML转换

gin也类似,gin提供 Must bind 和 Should bind 两种绑定方法,具体使用时根据 json,xml,yaml等使用不同的函数。两种绑定方法的区别在于当存在绑定错误时是否直接终止请求亦或者交由开发人员处理。

具体案例可参考
模型绑定和验证 官方
模型绑定和验证 中文
Json 数据解析和绑定

请求校验

goframe 和 gin 的校验方式形式上差不多,都支持自定义的校验模式,不同的是,goframe还支持将错误转换为错误接口,这样可以控制不一次性输出全部错误,一次性输出全部错误有时候对用户并不友好。诸如每次只输出第一个校验错误。

goframe 请求校验示例
gin 自定义校验示例
gin 结构体验证
gin 自定义验证

默认值绑定

goframe 和 gin 都支持默认值绑定,uri,query,form等都是支持默认值的。默认值绑定的一个常用场景是,列表分页时的每页默认条数和当前页数的赋值。

goframe 默认值绑定 示例

goframe支持json的默认值绑定,而gin并不能直接在结构体定义的时候设置默认值,可行的方法在实例化json时再自行设置默认值。gin官方文档中并没详细如何处理参数的默认值问题,所以我下面写个示例补充一下。

gin的uri,form等默认取值比较简单,使用 DefaultQuery 和 DefaultPostForm 即可解决默认值的问题

1
2
3
route.GET("/", func(c *gin.Context) {
c.DefaultQuery("name", "john")
})

或者,也可以在定义结构体的时候声明

1
2
3
4
type ListInfo struct {
Page int `form:"page,default=1" json:"page" xml:"page" `
Size int `form:"size,default=10" json:"size" xml:"size" `
}

gin设定 json 与 xml 的默认值

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
package main

import (
"net/http"

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

// Binding from JSON
type ListInfo struct {
Page int `form:"page,default=1" json:"page" xml:"page" `
Size int `form:"size,default=10" json:"size" xml:"size" `
}

func main() {
router := gin.Default()

// Example for binding JSON ({"user": "manu", "password": "123"})
router.POST("/listJSON", func(c *gin.Context) {
json := ListInfo{
Page: 1,
Size: 10,
}
if err := c.ShouldBindJSON(&json); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, json)
})

// Example for binding XML (
// <?xml version="1.0" encoding="UTF-8"?>
// <root>
// <page>1</page>
// <size>10</size>
// </root>)
router.POST("/listXML", func(c *gin.Context) {
xml := ListInfo{
Page: 1,
Size: 10,
}
if err := c.ShouldBindXML(&xml); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, xml)
})

// Listen and serve on 0.0.0.0:8080
router.Run(":8080")
}

自定义变量 与 Context

开发者可以在请求中自定义一些变量设置, goframe 和 gin 都支持。

goframe文档 自定义变量
goframe文档 Context
gin中间件