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

https://juejin.im/post/5cb9a47ff265da038b201575

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

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

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

  1. type Context struct {
  2. Request *http.Request
  3. Writer ResponseWriter
  4. Params Params
  5. // Keys is a key/value pair exclusively for the context of each request.
  6. Keys map[string]interface{}
  7. // Errors is a list of errors attached to all the handlers/middlewares who used this context.
  8. Errors errorMsgs
  9. // Accepted defines a list of manually accepted formats for content negotiation.
  10. Accepted []string
  11. // contains filtered or unexported fields
  12. }

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

获取请求参数

1. Path

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

  1. func (c *Context) Param(key string) string

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

  1. r.GET("/user/:id",func(c *gin.Context){
  2. id := c.Param("id")
  3. })

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

  1. type Params []Param
  2. func (ps Params) ByName(name string) (va string)
  3. func (ps Params) Get(name string) (string, bool)

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

  1. r.GET("/user/:id",func(c *gin.Context){
  2. id,err := c.Params.Get("id")
  3. //id := c.Params.ByName("id")
  4. })

2. Query

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

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

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

1. 获取单个参数
  1. func (c *Context) GetQuery(key string) (string, bool)
  2. func (c *Context) Query(key string) string
  3. func (c *Context) DefaultQuery(key, defaultValue string) string

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

示例如下:

  1. r.GET("/user", func(c *gin.Context) {
  2. id,_ := c.GetQuery("id")
  3. //id := c.Query("id")
  4. //id := c.DefaultQuery("id","10")
  5. c.JSON(200,id)
  6. })

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

响应:11

2. 获取数组

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

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

示例如下:

  1. r.GET("/user", func(c *gin.Context) {
  2. ids := c.QueryArray("id")
  3. //id,_ := c.QueryArray("id")
  4. c.JSON(200,ids)
  5. })

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

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

3. 获取map

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

  1. func (c *Context) QueryMap(key string) map[string]string
  2. func (c *Context) GetQueryMap(key string) (map[string]string, bool)

示例如下:

  1. r.GET("/user", func(c *gin.Context) {
  2. ids := c.QueryMap("ids")
  3. //ids,_ := c.GetQueryMap("ids")
  4. c.JSON(200,ids)
  5. })

请求: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的方法使用类型,区别只是数据来源不同而已,这里便不再写示例程序。

  1. func (c *Context) PostForm(key string) string
  2. func (c *Context) PostFormArray(key string) []string
  3. func (c *Context) PostFormMap(key string) map[string]string
  4. func (c *Context) DefaultPostForm(key, defaultValue string) string
  5. func (c *Context) GetPostForm(key string) (string, bool)
  6. func (c *Context) GetPostFormArray(key string) ([]string, bool)
  7. func (c *Context) GetPostFormMap(key string) (map[string]string, bool)
  8. func (c *Context) GetRawData() ([]byte, error)

数据绑定

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

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

  1. const (
  2. MIMEJSON = "application/json"
  3. MIMEHTML = "text/html"
  4. MIMEXML = "application/xml"
  5. MIMEXML2 = "text/xml"
  6. MIMEPlain = "text/plain"
  7. MIMEPOSTForm = "application/x-www-form-urlencoded"
  8. MIMEMultipartPOSTForm = "multipart/form-data"
  9. MIMEPROTOBUF = "application/x-protobuf"
  10. MIMEMSGPACK = "application/x-msgpack"
  11. MIMEMSGPACK2 = "application/msgpack"
  12. MIMEYAML = "application/x-yaml"
  13. )

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

  1. var (
  2. JSON = jsonBinding{}
  3. XML = xmlBinding{}
  4. Form = formBinding{}
  5. Query = queryBinding{}
  6. FormPost = formPostBinding{}
  7. FormMultipart = formMultipartBinding{}
  8. ProtoBuf = protobufBinding{}
  9. MsgPack = msgpackBinding{}
  10. YAML = yamlBinding{}
  11. Uri = uriBinding{}
  12. )

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

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

以Bind为前缀的系列方法

1. Path
  1. func (c *Context) BindUri(obj interface{}) error

代码示例:

  1. type User struct {
  2. Uid int //用户id
  3. Username string //用户名
  4. }
  5. func main() {
  6. r := gin.Default()
  7. r.GET("/bind/:uid/username", func(c *gin.Context) {
  8. var u User
  9. e := c.BindUri(&u)
  10. if e == nil{
  11. c.JSON(200,u)
  12. }
  13. })
  14. r.Run()
  15. }

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

输入:{1,"小张"}

2. Query
  1. func (c *Context) BindQuery(obj interface{}) error

代码示例:

  1. r.GET("/bind/:uid/username", func(c *gin.Context) {
  2. var u User
  3. e := c.BindQuery(&u)
  4. if e == nil{
  5. c.JSON(200,u)
  6. }
  7. })

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

输出:{1,"小张"}

3. Body

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

  1. func (c *Context) BindJSON(obj interface{}) error
  2. func (c *Context) BindXML(obj interface{}) error
  3. func (c *Context) BindYAML(obj interface{}) error

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

  1. func (c *Context) Bind(obj interface{}) error

示例

  1. r.POST("bind",func(c *gin.Context){
  2. u := User{}
  3. c.Bind(&u)
  4. })

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

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

  1. func (c *Context) BindWith(obj interface{}, b binding.Binding) error
  2. func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error

示例

  1. r.POST("bind",func(c *gin.Context){
  2. u := User{}
  3. c.BindWith(&u,binding.JSON)
  4. c.MustBindWith(&u,binding.JSON)
  5. })

以ShouldBind为前缀的系列方法

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

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

小结

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

ft_authoradmin  ft_create_time2019-04-26 18:03
 ft_update_time2019-04-26 18:03