上一篇记录了gin框架的搭建与路由的基本使用,本文主要记录一下路由处理过程中的参数传递问题,包含GET与POST传参,以及路径参数的使用。

GET 参数的获取

get 请求的参数写在url 中, 如 http://xxxx.com/path?id=1234&name=Manu&value=*111

gin 框架获取get参数主要有以下方法:

  • func (c *Context) Query(key string) string
  • func (c *Context) DefaultQuery(key, defaultValue string) string
  • func (c *Context) GetQuery(key string) (string, bool)

写一个处理类:

1
2
3
4
func (user) Get(c *gin.Context)  {
	name := c.Query("name")
	c.JSON(200, gin.H{"msg": "Hello User:"+ name})
}

当正常访问路由时,如 /name=yyx 时,会返回 msg: "Hello User: yyx", 如果不加name 参数,则name 为空字符串,返回 msg: "Hello User:"

有时我们需要在没有传参数的时候,有个默认的参数,那么此时就会用到DefaultQuery 方法

1
2
3
4
func (user) Get(c *gin.Context)  {
	name := c.DefaultQuery("name", "yyx")
	c.JSON(200, gin.H{"msg": "Hello User:"+ name})
}

这时如果不传name 参数的话,则会以默认的 yyx 代替。

QueryDefaultQuery 底层都是调用的GetQuery 方法,GetQuery 方法的第二个返回值,表示想要获取的参数是否存在,有了这个参数我们就可以做一些判断

1
2
3
4
5
6
7
func (user) Get(c *gin.Context)  {
	if name, ok := c.GetQuery("name"); ok{
		c.JSON(200, gin.H{"msg": "Hello User:"+ name})
	}else{
		c.JSON(200, gin.H{"msg": "name 参数缺失"})
	}
}

POST 参数的获取

GET 获取参数还是比较简单的,POST方式传递的参数更加复杂。post 传参主要以 application/x-www-form-urlencodedmultipart/form-data, 这两种可以通过在header中设置Content-Type来设置类型。

与GET参数相对应,post 参数也有三种获取的方法

  • func (c *Context) PostForm(key string) string
  • func (c *Context) DefaultPostForm(key, defaultValue string) string
  • func (c *Context) GetPostForm(key string) (string, bool)

大多数情况下使用的是application/x-www-form-urlencoded ,可以使用PostForm方法来接收

1
2
3
4
func (user) Post(c *gin.Context) {
	name := c.PostForm("name")
	c.JSON(200, gin.H{"msg": "hi,"+name})
}

如果没有传name参数的时候,可以设置一个默认值

1
2
3
4
func (user) Post(c *gin.Context) {
	name := c.DefaultPostForm("name", "杨彦星")
	c.JSON(200, gin.H{"msg": "hi,"+name})
}

也可以通过GetPostForm来判断参数是否存在

1
2
3
4
5
6
7
func (user) Post(c *gin.Context) {
	if name,ok := c.GetPostForm("name"); ok{
		c.JSON(200, gin.H{"msg": "hi,"+name})
	}else{
		c.JSON(200, gin.H{"msg": "参数缺失"})
	}
}

参数与结构体进行绑定

以上的get或者是post 中获取参数值的方法都是一个一个的依次赋值,当参数比较多的时候会很麻烦,这时可以定义一个结构体,然后将参数绑定到结构中

如定义student 结构体

1
2
3
4
type student struct {
	Name string `json:"name" form:"name"`
	Age int `json:"age" form:"age"`
}

这里在定义结构体的时候,添加了tag,这样参数以json或者form的形式就都可以识别了

1
2
3
4
5
6
7
8
func (user) Post(c *gin.Context) {
	s := student{}
	if err := c.ShouldBind(&s); err==nil{
		c.JSON(200, gin.H{"msg": "hi,"+s.Name})
	}else{
		c.JSON(200, gin.H{"msg": "参数缺失"})
	}
}

可以使用postman来调用一下,下面是以json格式发送的请求

image-20220104155307867

下面是以form的形式发送

image-20220104155438988

使用结构体进行绑定的时候 ,如果没有传参数,是不会报错了,只是以对应类型的默认值来代替,以下当不传age参数的时候,值默认为0

image-20220104155652115

restful 风格的url

restful 风格的url, 通常将资源信息写到url中,如 /user/:id , 这种情况不可能为每一个id都对添加路由,这时我们可以使用通配符路由

需要两步,第一步设置路由信息

1
2
3
4
5
6
7
8
9
func main() {
	r := gin.Default()
	userH:= handlers.NewUser()
	userG := r.Group("/user")
	userG.GET("/list", userH.Get)
	userG.POST("/post", userH.Post)
	userG.GET(":id/info", userH.Info)  //这里设置通配符路由
	r.Run(":8080")
}

这里设置:id 为要匹配的路由参数,然后在相应的处理类中获取id参数

1
2
3
4
func (user) Info(c *gin.Context)  {
	id := c.Param("id")
	c.JSON(200, gin.H{"msg": "用户id为"+id})
}

当然还可以设置多个参数 userG.GET(":name/:age/info", userH.Infos)

但是这种路由参数需要注意一些问题,如果定义了如下的路由

1
2
userG.GET(":name/:age/info", userH.Infos)
userG.GET(":id/infos", userH.Info)

那么将启动失败,提示 panic: ':id' in new path '/user/:id/infos' conflicts with existing wildcard ':name' in existing prefix '/user/:name'

它按照上面的路径进行匹配,发现 name和id处于相同的位置 ,所以会提示id这个参数不能再被定义了,也就是说某一位置的参数只能定义一次,不能重复定义。

第二个问题,你可以定义如下的路由

userG.GET("abc:id/infos", userH.Info) 那么在路径匹配上,则可以匹配 /abc10/infos ,id则为10,不加abc字符串,则无法匹配。

以上为记录一些gin框架上获取参数的常用方法,但是还有一些问题,如在参数匹配过程中,如果要求age参数为int值,且在0-100之间,则目前还没法校验,只能在相应的处理类中去做判断,接下来的文章将记录一下参数校验器的使用。