Gin框架系列02:路由与参数
回顾#
上一节我们用Gin框架快速搭建了一个GET请求的接口,今天来学习路由和参数的获取。
请求动词#
熟悉RESTful
的同学应该知道,RESTful
是网络应用程序的一种设计风格和开发方式,每一个URI代表一种资源,客户端通过POST
、DELETE
、PUT
、GET
四种请求方式来对资源做增删改查的操作。
同样的,Gin框架给我们提供的除这4种动词外,还有PATCH
、OPTION
、HEAD
等,详细内容可以查看rentergroup.go
文件的IRoutes
接口。
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
实例化对象,接下来所有的路由操作都通过该对象使用即可。
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()
}
请求动词的第一个参数是请求路径,第二个参数是用于逻辑处理的函数,可以是匿名的或是其他地方定义的函数名。不同的请求动词可以定义相同的路径,只需要切换动词就可以进入对应的处理逻辑。
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
,这种没有参数名,需要在代码中解析参数。
protocol://hostname:[port]/path/[query]#fragment
我们先来看路由携带参数值的玩法,这里有一道题,怎么利用Gin获取下面链接的参数值1
。
实现方式非常简单,只需要在路由中设置好占位符:id
,冒号为占位符的标志,冒号后面的参数名可以自定义,Gin会将路由与请求地址进行匹配,若匹配成功会将1
赋值为占位符:id
,只需调用c.Param
就可以获取id
的值。
router.GET("/article/:id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, id)
})
但是,:id
占位符会存在一个问题,如果id
参数值传空就会有404
的错误提示。
于是Gin提供了另一种占位符*id
,使用它就可以达到取空值的目的。
router.GET("/article/*id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, id)
})
普通参数#
除了路由携带参数值外,接下来看比较传统的GET
传参方式。
http://localhost:8080/welcome?firstname=Jane&lastname=Doe
可以通过c.Query
或c.DefaultQuery
方法获取问号后的参数。
router.GET("/welcome", func(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "pingyeaa")
lastname := c.Query("lastname")
c.String(200, firstname+" "+lastname)
})
这两者最终都调用了GetQuery
方法,唯一的区别是DefaultQuery
做了默认值处理。
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
内容同样也可以轻松获取。
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,
})
})
curl -d "message=pingye" http://localhost:8080/form_post
{"message":"pingye","nick":"anonymous","status":"posted"}
数组类型参数#
有时候(例如复选框)前端页面会传来数组类型的值,这种类型name
相同,但存储的内容不同。
POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
Content-Type: application/x-www-form-urlencoded
依然是一个QueryMap
方法就搞定,该方法默认返回map
类型。
router.GET("/post", func(c *gin.Context) {
ids := c.QueryMap("ids")
c.String(200, ids["a"]+" "+ids["b"])
})
curl http://localhost:8080/post?ids[a]=pingye&ids[b]=hehe
pingye hehe
文件上传#
一般情况下,文件上传会由前端直接传给云存储服务商,比如阿里云、七牛云等,比较少的场景会传给自己的服务器。为了避免书到用时方恨少
的情况发生,我们来了解一下。
Gin提供了FormFile
方法获取文件流,这个方法返回了一个FileHeader
类型的变量,可以调用Filename
属性来查看文件名。
type FileHeader struct {
Filename string
Header textproto.MIMEHeader
Size int64
content []byte
tmpfile string
}
router.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
c.String(200, file.Filename)
})
通过curl
请求接口,可以看到轻松获取文件名称。
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
方法将文件保存到某个地方,文件保存时要确保有目标目录的操作权限。
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
这种形式。
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")
})
}
curl -X POST http://localhost:8080/v1/login
curl -X POST http://localhost:8080/v2/login
Go语言库代码示例,欢迎star
https://github.com/pingyeaa/golang-examples#
相关文章
- thinkPHP5框架路由常用知识点汇总
- 第三百零四节,Django框架,urls.py模块,views.py模块,路由映射与路由分发以及逻辑处理——url控制器
- 第二百六十八节,Tornado框架-路由映射之二级域名支持,html模板继承以及导入
- CCNA2.0笔记_动态路由
- RabbitMQ消息队列(五):Routing 消息路由
- flask框架-路由
- django框架基础-路由系统-长期维护
- ASP.NET Core 6框架揭秘实例演示[31]:路由高阶用法
- 通过极简模拟框架让你了解ASP.NET Core MVC框架的设计与实现[上篇]:路由整合
- SAP Spartacus B2B Unit List跳转到Unit Detail页面的路由执行逻辑
- Atitit.面向接口的web 原理与设计重写 路由启动绑定配置url router rewriting urlpage mvc mvp的 java c#.net php js
- SAP 电商云 Spartacus UI product 明细页面的路由配置
- vue项目router-link怎样获取动态路由怎样传参?怎样获取/:id的值?
- vuejs项目:路由跳转时更改页面title名称
- 【项目实战】Spring Cloud Gateway入门介绍 - 路由断言工厂
- vert.x框架-简单路由使用
- 【Android Gradle 插件】组件化中的 Gradle 构建脚本实现 ④ ( 使用路由实现组件间通信 | 引入 ARoute 框架 | Gradle 构建脚本优化问题 )
- 小程序开发之路由跳转
- 第四十八讲:神州路由器IPv6静态路由配置
- 第二十一讲:神州路由器RIP路由的配置
- golangWeb框架---github.com/gin-gonic/gin学习三(路由多视图函数、中间件、log日志)
- golangWeb框架---github.com/gin-gonic/gin学习一(路由、多级路由、参数传递几种形式)
- beego框架学习(二) -路由设置
- 【Android源码解析】一篇就够“路由、网络层、UI层、通信层....百大框架”源码解析,阿里P8神级之作
- 动态路由协议 — RIPv1配置
- IPSec基于路由建立隧道---预共享密钥认证方式实验(安全框架)