在 上一篇 Golang Gin 实战(二)| 简便的Restful API 实现 文章中,我们留了一个疑问,假如我们有很多用户,我们要为他们一个个注册路由(路径)吗?
如下URL:
/users/123
/users/456
/users/23456
以上等等,我们有很多用户,如果我们都一个个为这些用户注册这些路由(URL),那么我们是很难注册完的,而且我们还会有新注册的用户,可见这种办法不行。
我们观察这些路由(URL),发现它们具备一定的规则:前面都是users
,后面是users
的id
。这样我们就可以把这些路由归纳为:
/users/id
这样我们就知道只有id
这部分是可以变的,前面的users
是不变的。可变的id
可以当成我们API服务输入的参数,这样我们就可以通过这个id
参数,获取对应的用户信息,这种URL匹配的模式,我们称之为路由参数。
在Gin
中,要实现以上路由参数非常简单:
func main() {
r := gin.Default()
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, "The user id is %s", id)
})
r.Run(":8080")
}
我们运行如上代码,打开浏览器,输入http://localhost:8080/users/123
,就可以看到如下信息:
The user id is 123
我们可以更换http://localhost:8080/users/123
中的id 123
为其他字符串,会发现都可以正常打印,这就是路由匹配、路由正则,或者路由参数。
Gin
的路由采用的是httprouter
,所以它的路由参数的定义和httprouter
也是一样的。
/users/:id
就是一种路由匹配模式,也是一个通配符,其中:id
就是一个路由参数,我们可以通过c.Param("id")
获取定义的路由参数的值,然后用来做事情,比如打印出来。
/users/:id
这种匹配模式是精确匹配的,只能匹配一个,我们举几个例子说明:
Pattern: /users/:id
/users/123 匹配
/users/哈哈 匹配
/users/123/go 不匹配
/users/ 不匹配
这里我故意写了/users/哈哈
,并且是匹配的,意思就是对于Gin
路径中的匹配都是字符串,它是不区分数字、字母和汉字的,都匹配。
这里还需要说明的是,Gin
的路由是单一的,不能有重复。比如这里我们注册了/users/:id
,那么我们就不能再注册匹配/users/:id
模式的路由,比如:
r.GET("/users/list", func(c *gin.Context) {
//省略无关代码
})
这时候我们运行程序的话,会出现如下提示:
panic: 'list' in new path '/users/list' conflicts with existing wildcard ':id' in existing prefix '/users/:id'
通配符重复了,路由必须要唯一。Gin
内部使用的路由是httprouter
,我这里前段时间正好有一篇关于httprouter
的详细分析,可以看下。Go语言经典库使用分析(七)| 高性能可扩展 HTTP 路由 httprouter
上面我们介绍的是:
号的路由参数,这种路由参数最常用。还有一种不常用的就是*
号类型的参数,表示匹配所有。
以/users/*id
为例:
Pattern: /users/*id
/users/123 匹配
/users/哈哈 匹配
/users/123/go 匹配
/users/ 匹配
我们把上面的例子改下:
func main() {
r := gin.Default()
r.GET("/users/*id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, "The user id is %s", id)
})
r.Run(":8080")
}
现在我们运行,浏览器里访问http://localhost:8080/users/123
,会看到如下信息:
The user id is /123
是否发现区别了,我们获取到的id
不是123
了,而是/123
,多了一个/
同样的你试试http://localhost:8080/users/123/go
会发现显示的信息是:
The user id is /123/go
是一个/
开头的路径字符串。
这里要特别说明一点的是,如果你用浏览器访问http://localhost:8080/users
,会被重定向到http://localhost:8080/users/
,然后显示的信息如下:
The user id is /
重定向的根本原因在于/users
没有匹配的路由,但是有匹配/users/
的路由,所以就会被重定向到/users/
。现在我们注册一个/users
来验证下这个猜测:
r.GET("/users", func(c *gin.Context) {
c.String(200, "这是真正的/users")
})
现在再访问http://localhost:8080/users
,会看到显示的信息变成了:
这是真正的/users
这也间接证明了/users/*id
和/users
这两个路由是不冲突的,可以被Gin
注册。
以上自动重定向的原理,得益于gin.RedirectTrailingSlash
等于true
的配置。如果我们把它改为false
就不会自动重定向了。
func main() {
r := gin.Default()
r.RedirectTrailingSlash = false
r.GET("/users/*id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, "The user id is %s", id)
})
r.Run(":8080")
}
现在我们运行程序,访问http://localhost:8080/users
发现显示的信息是404 page not found
这一篇主要介绍路由参数,并且基于这种参数,我们可以很灵活的实现我们的API,并且从路径中获取相应的参数进行操作。对于*
号参数,不建议使用,因为匹配的太多,会导致我们自己搞不清楚哪些路由被注册了。
除了路由参数,还有URL的query参数,也就是?a=b&c=d
这样的格式,下一篇文章我们再介绍。