前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Gin框架系列02:路由与参数

Gin框架系列02:路由与参数

作者头像
平也
发布2020-04-10 18:03:20
发布2020-04-10 18:03:20
1.7K00
代码可运行
举报
文章被收录于专栏:平也平也
运行总次数:0
代码可运行

回顾

上一节我们用Gin框架快速搭建了一个GET请求的接口,今天来学习路由和参数的获取。

请求动词

熟悉RESTful的同学应该知道,RESTful是网络应用程序的一种设计风格和开发方式,每一个URI代表一种资源,客户端通过POSTDELETEPUTGET四种请求方式来对资源做增删改查的操作。

同样的,Gin框架给我们提供的除这4种动词外,还有PATCHOPTIONHEAD等,详细内容可以查看rentergroup.go文件的IRoutes接口。

代码语言:javascript
代码运行次数:0
复制
type IRoutes interface {
	Use(...HandlerFunc) IRoutes

	Handle(string, string, ...HandlerFunc) IRoutes
	Any(string, ...HandlerFunc) IRoutes
	GET(string, ...HandlerFunc) IRoutes
	POST(string, ...HandlerFunc) IRoutes
	DELETE(string, ...HandlerFunc) IRoutes
	PATCH(string, ...HandlerFunc) IRoutes
	PUT(string, ...HandlerFunc) IRoutes
	OPTIONS(string, ...HandlerFunc) IRoutes
	HEAD(string, ...HandlerFunc) IRoutes

	StaticFile(string, string) IRoutes
	Static(string, string) IRoutes
	StaticFS(string, http.FileSystem) IRoutes
}

因为RenterGroup实现了IRoutes定义的所有请求动词,而且gin.Default返回的Engine类型继承了RenterGroup,所以使用起来非常简单,只需要通过gin.Default实例化对象,接下来所有的路由操作都通过该对象使用即可。

代码语言:javascript
代码运行次数:0
复制
func main() {
	router := gin.Default()
	router.POST("/article", func(c *gin.Context) {
		c.String(200, "article post")
	})
	router.DELETE("/article", func(c *gin.Context) {
		c.String(200, "article delete")
	})
	router.PUT("/article", func(c *gin.Context) {
		c.String(200, "article put")
	})
	router.GET("/article", func(c *gin.Context) {
		c.String(200, "article get")
	})
	router.Run()
}

请求动词的第一个参数是请求路径,第二个参数是用于逻辑处理的函数,可以是匿名的或是其他地方定义的函数名。不同的请求动词可以定义相同的路径,只需要切换动词就可以进入对应的处理逻辑。

代码语言:javascript
代码运行次数:0
复制
curl -X PUT http://localhost:8080/article
curl -X POST http://localhost:8080/article
curl -X GET http://localhost:8080/article
curl -X DELETE http://localhost:8080/article

路由参数

GET请求有两种,一种是在URL后加上?name=pingye,这种是有参数名的,另一种是在路径中直接加上参数值/article/1,这种没有参数名,需要在代码中解析参数。

代码语言:javascript
代码运行次数:0
复制
protocol://hostname:[port]/path/[query]#fragment

我们先来看路由携带参数值的玩法,这里有一道题,怎么利用Gin获取下面链接的参数值1

实现方式非常简单,只需要在路由中设置好占位符:id,冒号为占位符的标志,冒号后面的参数名可以自定义,Gin会将路由与请求地址进行匹配,若匹配成功会将1赋值为占位符:id,只需调用c.Param就可以获取id的值。

代码语言:javascript
代码运行次数:0
复制
router.GET("/article/:id", func(c *gin.Context) {
  id := c.Param("id")
  c.String(200, id)
})

但是,:id占位符会存在一个问题,如果id参数值传空就会有404的错误提示。

于是Gin提供了另一种占位符*id,使用它就可以达到取空值的目的。

代码语言:javascript
代码运行次数:0
复制
router.GET("/article/*id", func(c *gin.Context) {
  id := c.Param("id")
  c.String(200, id)
})

普通参数

除了路由携带参数值外,接下来看比较传统的GET传参方式。

代码语言:javascript
代码运行次数:0
复制
http://localhost:8080/welcome?firstname=Jane&lastname=Doe

可以通过c.Queryc.DefaultQuery方法获取问号后的参数。

代码语言:javascript
代码运行次数:0
复制
router.GET("/welcome", func(c *gin.Context) {
   firstname := c.DefaultQuery("firstname", "pingyeaa")
   lastname := c.Query("lastname")
   c.String(200, firstname+" "+lastname)
})

这两者最终都调用了GetQuery方法,唯一的区别是DefaultQuery做了默认值处理。

代码语言:javascript
代码运行次数:0
复制
func (c *Context) DefaultQuery(key, defaultValue string) string {
	if value, ok := c.GetQuery(key); ok {
		return value
	}
	return defaultValue
}

func (c *Context) Query(key string) string {
	value, _ := c.GetQuery(key)
	return value
}

表单参数

从HTML提交过来的表单form内容同样也可以轻松获取。

代码语言:javascript
代码运行次数:0
复制
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,
  })
})
代码语言:javascript
代码运行次数:0
复制
curl -d "message=pingye" http://localhost:8080/form_post
{"message":"pingye","nick":"anonymous","status":"posted"}

数组类型参数

有时候(例如复选框)前端页面会传来数组类型的值,这种类型name相同,但存储的内容不同。

代码语言:javascript
代码运行次数:0
复制
POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
Content-Type: application/x-www-form-urlencoded

依然是一个QueryMap方法就搞定,该方法默认返回map类型。

代码语言:javascript
代码运行次数:0
复制
router.GET("/post", func(c *gin.Context) {
  ids := c.QueryMap("ids")
  c.String(200, ids["a"]+" "+ids["b"])
})
代码语言:javascript
代码运行次数:0
复制
curl http://localhost:8080/post?ids[a]=pingye&ids[b]=hehe
pingye hehe

文件上传

一般情况下,文件上传会由前端直接传给云存储服务商,比如阿里云、七牛云等,比较少的场景会传给自己的服务器。为了避免书到用时方恨少的情况发生,我们来了解一下。

Gin提供了FormFile方法获取文件流,这个方法返回了一个FileHeader类型的变量,可以调用Filename属性来查看文件名。

代码语言:javascript
代码运行次数:0
复制
type FileHeader struct {
   Filename string
   Header   textproto.MIMEHeader
   Size     int64

   content []byte
   tmpfile string
}
代码语言:javascript
代码运行次数:0
复制
router.POST("/upload", func(c *gin.Context) {
  file, _ := c.FormFile("file")
  c.String(200, file.Filename)
})

通过curl请求接口,可以看到轻松获取文件名称。

代码语言:javascript
代码运行次数:0
复制
curl -X POST http://localhost:8080/upload \
  -F "file=@/Users/enoch/Downloads/IMG_9216.JPG" \
  -H "Content-Type: multipart/form-data"
IMG_9216.JPG

当然不止可以拿到文件名,我们还可以使用SaveUploadedFile方法将文件保存到某个地方,文件保存时要确保有目标目录的操作权限。

代码语言:javascript
代码运行次数:0
复制
router.POST("/upload", func(c *gin.Context) {
		file, _ := c.FormFile("file")
		c.String(200, file.Filename)
		err := c.SaveUploadedFile(file, "/Users/enoch/Desktop/ab.png")
		if err != nil {
			c.String(500, err.Error())
		}
})

路由分组

当接口发生重大变更(比如入参出参)时,考虑到向下兼容,一般会新增一个接口,但是又希望新接口的名称显而易见地看出是老接口的升级版,那么就可以在接口名前加上版本号v1/article这种形式。

代码语言:javascript
代码运行次数:0
复制
v1 := r.Group("v1")
{
  v1.POST("/login", func(c *gin.Context) {
    c.String(200, "v1/login")
  })
  v1.POST("/submit", func(c *gin.Context) {
    c.String(200, "v1/submit")
  })
}

v2 := r.Group("v2")
{
  v2.POST("/login", func(c *gin.Context) {
    c.String(200, "v2/login")
  })
  v2.POST("/submit", func(c *gin.Context) {
    c.String(200, "v2/submit")
  })
}
代码语言:javascript
代码运行次数:0
复制
curl -X POST http://localhost:8080/v1/login
curl -X POST http://localhost:8080/v2/login
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-04-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 回顾
  • 请求动词
  • 路由参数
  • 普通参数
  • 表单参数
  • 数组类型参数
  • 文件上传
  • 路由分组
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档