Go Web轻量级框架Gin学习系列:数据绑定

前面写了两篇与Gin框架学习有关的文章,主要讲了Gin框架的安装,定义处理HTTP请求的各种方法以及如何根据客户端需求返回不同格式的数据,但这中间漏了一个环节,那就是返回数据之前,如何获取客户端HTTP请求中带上来的参数,关于这点,我们就在这篇文章中讲一讲。

Gin框架将处理HTTP请求参数以及如何响应等操作都封装到了gin.Conetxt结构体,并为gin.Context提供了非常多的方法,因此了解gin.Context的结构定义与方法,对使用Gin框架编写Web项目非常重要。

下面是gin.Context结构定义代码:

type Context struct {
    Request *http.Request
    Writer  ResponseWriter
    Params Params
    // Keys is a key/value pair exclusively for the context of each request.
    Keys map[string]interface{}
    // Errors is a list of errors attached to all the handlers/middlewares who used this context.
    Errors errorMsgs
    // Accepted defines a list of manually accepted formats for content negotiation.
    Accepted []string
    // contains filtered or unexported fields
}
复制代码

从上面的gin.Context的结构定义来看,gin.Context封装了http.Requesthttp.ResponseWriter

获取请求参数

1. Path

path是指请求的url中域名之后从/开始的部分,如掘金首页地址:https://juejin.im/timeline/timeline部分便是path,可以使用gin.Context中的Param()方法获取这部分参数。

func (c *Context) Param(key string) string
复制代码

使用Param()方法获取path中的参数:

r.GET("/user/:id",func(c *gin.Context){
    id := c.Param("id")
})
复制代码

除了使用gin.Context的中Param()方法外,还可以用gin.Context中的Params字段获取到path中的参数,Params的定义如下:

type Params []Param
func (ps Params) ByName(name string) (va string)
func (ps Params) Get(name string) (string, bool)
复制代码

使用gin.Context中的Params字段获取path中的参数示例如下:

r.GET("/user/:id",func(c *gin.Context){
    id,err := c.Params.Get("id")
    //id := c.Params.ByName("id")
})
复制代码
2. Query

query是指url请求地址中的问号后面的部,称为查询参数,如下面地址中,query=%E6%96%87%E7%AB%A0&type=all就是查询参数。

https://juejin.im/search?query=%E6%96%87%E7%AB%A0&type=all
复制代码

gin.Context提供了以下几个方法,用于获取Query部分的参数。

1. 获取单个参数
func (c *Context) GetQuery(key string) (string, bool)
func (c *Context) Query(key string) string
func (c *Context) DefaultQuery(key, defaultValue string) string
复制代码

上面三个方法用于获取单个数值,GetQueryQuery多返回一个error类型的参数,实际上Query方法只是封装了GetQuery方法,并忽略GetQuery方法返回的错误而已,而DefaultQuery方法则在没有获取相应参数值的返回一个默认值。

示例如下:

r.GET("/user", func(c *gin.Context) {
    id,_ := c.GetQuery("id")
    //id := c.Query("id")
    //id := c.DefaultQuery("id","10")
    c.JSON(200,id)
})
复制代码

请求:http://localhost:8080/user?id=11

响应:11

2. 获取数组

GetQueryArray方法和QueryArray的区别与GetQuery和Query的相似。

func (c *Context) GetQueryArray(key string) ([]string, bool)
func (c *Context) QueryArray(key string) []string

复制代码

示例如下:

r.GET("/user", func(c *gin.Context) {
    ids := c.QueryArray("id")
    //id,_ := c.QueryArray("id")
    c.JSON(200,ids)
})
复制代码

请求:http://localhost:8080/user?id=10&id=11&id=12

响应:["10","11","12"]

3. 获取map

GetQueryArray方法和QueryArray的区别与GetQuery和Query的相似。

func (c *Context) QueryMap(key string) map[string]string
func (c *Context) GetQueryMap(key string) (map[string]string, bool)
复制代码

示例如下:

r.GET("/user", func(c *gin.Context) {
    ids := c.QueryMap("ids")
    //ids,_ := c.GetQueryMap("ids")
    c.JSON(200,ids)
})
复制代码

请求:http://localhost:8080/user?ids[10]=zhang

响应:{"10":"zhang"}

3. Body

一般HTTP的Post请求参数都是通过body部分传给服务器端的,尤其是数据量大或安全性要求较高的数据,如登录功能中的账号密码等参数。

gin.Context提供了以下四个方法让我们获取body中的数据,不过要说明的是,下面的四个方法,只能获取Content-typeapplication/x-www-form-urlencodedmultipart/form-databody中的数据。

下面方法的使用方式与上面获取Query的方法使用类型,区别只是数据来源不同而已,这里便不再写示例程序。

func (c *Context) PostForm(key string) string
func (c *Context) PostFormArray(key string) []string
func (c *Context) PostFormMap(key string) map[string]string
func (c *Context) DefaultPostForm(key, defaultValue string) string
func (c *Context) GetPostForm(key string) (string, bool)
func (c *Context) GetPostFormArray(key string) ([]string, bool)
func (c *Context) GetPostFormMap(key string) (map[string]string, bool)
func (c *Context) GetRawData() ([]byte, error)
复制代码

数据绑定

在前面的例子中,我们直接使用gin.Context提供的方法获取请求中通过pathquerybody带上来的参数,但使用前面的那些方法,并不能处理请求中比较复杂的数据结构,比如Content-type为application/json或application/xml时,其所带上的数据会很复杂,因此我们需要使用另外一种方法获取这些数据,这种方式叫数据绑定

Gin框架将数据绑定的操作都封装在gin/binding这个包中,下面是gin/binding包定义的常量,说明gin/binding包所支持的Content-type格式。

const (
    MIMEJSON              = "application/json"
    MIMEHTML              = "text/html"
    MIMEXML               = "application/xml"
    MIMEXML2              = "text/xml"
    MIMEPlain             = "text/plain"
    MIMEPOSTForm          = "application/x-www-form-urlencoded"
    MIMEMultipartPOSTForm = "multipart/form-data"
    MIMEPROTOBUF          = "application/x-protobuf"
    MIMEMSGPACK           = "application/x-msgpack"
    MIMEMSGPACK2          = "application/msgpack"
    MIMEYAML              = "application/x-yaml"
)
复制代码

gin.binding包也定义处理不同Content-type提交数据的处理结构体,并以变量的形式让其他包可以访问,如下:

var (
    JSON          = jsonBinding{}
    XML           = xmlBinding{}
    Form          = formBinding{}
    Query         = queryBinding{}
    FormPost      = formPostBinding{}
    FormMultipart = formMultipartBinding{}
    ProtoBuf      = protobufBinding{}
    MsgPack       = msgpackBinding{}
    YAML          = yamlBinding{}
    Uri           = uriBinding{}
)
复制代码

但实际上,我们并不需要调用gin/binding包的代码来完成数据绑定的功能,因为gin.Context中已经在gin.Context的基础上封装了许多更加快捷的方法供我们使用:

gin.Context封装的相关绑定方法,分为以Bind为前缀的系列方法和以ShouldBind为前缀的系列方法,这两个系列方法之间的差别在于以Bind为前缀的方法,在用户输入数据不符合相应格式时,会直接返回http状态为400的响应给客户端。

以Bind为前缀的系列方法
1. Path
func (c *Context) BindUri(obj interface{}) error
复制代码

代码示例:

type User struct {
    Uid      int    //用户id
    Username string //用户名
}
func main() {
    r := gin.Default()
    r.GET("/bind/:uid/username", func(c *gin.Context) {
        var u User
        e := c.BindUri(&u)
        if e == nil{
            c.JSON(200,u)
        }
    })
    r.Run()
}

复制代码

请求:http://localhost:8080/bind/1/小张

输入:{1,"小张"}

2. Query
func (c *Context) BindQuery(obj interface{}) error
复制代码

代码示例:

r.GET("/bind/:uid/username", func(c *gin.Context) {
    var u User
    e := c.BindQuery(&u)
    if e == nil{
        c.JSON(200,u)
    }
})
复制代码

请求:http://localhost:8080/bind?uid=1&username=小张

输出:{1,"小张"}

3. Body

当我们在HTTP请求中Body设置不同数据格式,需要设置相应头部Content-Type的值,比较常用为jsonxmlyamlgin.Context提供下面三个方法绑定对应Content-type时body中的数据。

func (c *Context) BindJSON(obj interface{}) error
func (c *Context) BindXML(obj interface{}) error
func (c *Context) BindYAML(obj interface{}) error
复制代码

除了上面三个方法外,更常用的Bind()方法,Bind()方法会自动根据Content-Type的值选择不同的绑定类型。

func (c *Context) Bind(obj interface{}) error
复制代码

示例

r.POST("bind",func(c *gin.Context){
    u := User{}
    c.Bind(&u)
})
复制代码

上面几个方法都是获取固定Content-type或自动根据Content-type选择绑定类型,我们也可以使用下面两个方法自行选择绑定类型。

下面两个方法的第二个参数值是gin.binding中定义好的常量,我们在上面讲过。

func (c *Context) BindWith(obj interface{}, b binding.Binding) error
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error
复制代码

示例

r.POST("bind",func(c *gin.Context){
	u := User{}
	c.BindWith(&u,binding.JSON)
    c.MustBindWith(&u,binding.JSON)
})
复制代码
以ShouldBind为前缀的系列方法

以ShouldBind为前缀的相应的方法与以Bind为前缀的方法使用基本相同,因此下面没有相应演示的代码。

1. Path
func (c *Context) ShouldBindUri(obj interface{}) error
复制代码
2. Query
func (c *Context) ShouldBindQuery(obj interface{}) error
复制代码
3. Body
func (c *Context) ShouldBind(obj interface{}) error
func (c *Context) ShouldBindJSON(obj interface{}) error
func (c *Context) ShouldBindXML(obj interface{}) error
func (c *Context) ShouldBindYAML(obj interface{}) error
func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error)
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error
复制代码

小结

Gin框架在net/http包的基础上封装了许多的方法,让我们可以接收客户端传递上来的各种不同格式的数据,但是从客户端得到的数据之后,还是要验证数据是否合法或是否我们想要的,这是Gin框架中有关数据验证器的知识了,有机会再写写这方面的文章。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值