goframe
框架自建了非常强大的路由功能,提供了比任何同类框架更加出色的路由特性,支持流行的命名匹配规则、模糊匹配规则及字段匹配规则,并提供了优秀的优先级管理机制。
一个示例
在真正开启本章的核心内容之前,我们先来看一个简单的动态路由使用示例:
package main
import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/frame/g"
)
func main() {
s := g.Server()
s.BindHandler("/:name", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/:name/update", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/:name/:action", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/:name/*any", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.BindHandler("/user/list/{field}.html", func(r *ghttp.Request){
r.Response.Writeln(r.Router.Uri)
})
s.SetPort(8199)
s.Run()
}
以上示例中展示了 goframe
框架支持的三种模糊匹配路由规则, :name
、 *any
、 {field}
分别表示 命名匹配规则、 模糊匹配规则 及 字段匹配规则。不同的规则中使用 /
符号来划分层级,路由检索采用深度优先算法,层级越深的规则优先级也会越高。我们运行以上示例,通过访问几个URL来看看效果:
URL 结果
http://127.0.0.1:8199/user/list/2.html /user/list/{field}.html
http://127.0.0.1:8199/user/update /:name/update
http://127.0.0.1:8199/user/info /:name/:action
http://127.0.0.1:8199/user /:name/*any
在这个示例中我们也可以看到,由于优先级的限制,路由规则 /:name
会被 /:name/*any
规则覆盖,将会无法被匹配到,所以在分配路由规则的时候,需要进行统一规划和管理,避免类似情况的产生。
注册规则
路由注册参数
我们来看一下之前一直使用的 BindHandler
的原型:
func (s *Server) BindHandler(pattern string, handler interface{})
该方法是路由注册的最基础方法,其中的 pattern
为路由注册规则字符串,在其他路由注册方法中也会使用到,参数格式如下:
[HTTPMethod:]路由规则[@域名]
其中 HTTPMethod
(支持的Method: GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE
)和 @域名
为非必需参数,一般来说直接给定路由规则参数即可, BindHandler
会自动绑定 所有的 请求方式,如果给定 HTTPMethod
,那么路由规则仅会在该请求方式下有效。 @域名
可以指定生效的域名名称,那么该路由规则仅会在该域名下生效。
BindHandler
是最原生的路由注册方法,在大部分场景中,我们通常使用 分组路由 方式来管理理由,详见: 路由注册-分组路由。
我们来看一个例子:
package main
import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/frame/g"
)
func main() {
s := g.Server()
// 该路由规则仅会在GET请求下有效
s.BindHandler("GET:/{table}/list/{page}.html", func(r *ghttp.Request){
r.Response.WriteJson(r.Router)
})
// 该路由规则仅会在GET请求及localhost域名下有效
s.BindHandler("GET:/order/info/{order_id}@localhost", func(r *ghttp.Request){
r.Response.WriteJson(r.Router)
})
// 该路由规则仅会在DELETE请求下有效
s.BindHandler("DELETE:/comment/{id}", func(r *ghttp.Request){
r.Response.WriteJson(r.Router)
})
s.SetPort(8199)
s.Run()
}
其中返回的参数 r.Router
是当前匹配的路由规则信息,访问当该方法的时候,服务端会输出当前匹配的路由规则信息。执行后,我们在终端使用 curl
命令进行测试:
$ curl -XGET http://127.0.0.1:8199/order/list/1.html
{"Domain":"default","Method":"GET","Priority":3,"Uri":"/{table}/list/{page}.html"}
$ curl -XGET http://127.0.0.1:8199/order/info/1
Not Found
$ curl -XGET http://localhost:8199/order/info/1
{"Domain":"localhost","Method":"GET","Priority":3,"Uri":"/order/info/{order_id}"}
$ curl -XDELETE http://127.0.0.1:8199/comment/1000
{"Domain":"default","Method":"DELETE","Priority":2,"Uri":"/comment/{id}"}
$ curl -XGET http://127.0.0.1:8199/comment/1000
Not Found
值得说明的是,在大多数场景下,我们很少直接在路由规则中使用 @域名
这样的规则来限定路由注册的域名,而是使用 ghttp.Server.Domain(domains string)
方法来获得指定域名列表的管理对象,随后使用该域名对象进行路由注册,域名对象即可实现对指定域名的绑定操作。
精准匹配规则
精准匹配规则即 未使用任何动态规则 的规则,如: user
、 order
、 info
等等这种 确定名称 的规则。在大多数场景下,精准匹配规则会和动态规则一起使用来进行路由注册(例如: /:name/list
,其中层级1 :name
为命名匹配规则,层级2 list
是精准匹配规则)。
动态路由规则
动态路由规则分为三种: 命名匹配规则、 模糊匹配规则 和 字段匹配规则。动态路由的底层数据结构是由层级 哈希表
和 双向链表
构建的 路由树
,层级哈希表便于高效率地层级匹配 URI
;数据链表用于优先级控制,同一层级的路由规则按照优先级进行排序,优先级高的规则排在链表头。底层的路由规则与请求 URI
的匹配计算采用的是正则表达式,并充分使用了缓存机制,执行效率十分高效。
所有匹配到的参数都将会以 Router
参数的形式传递给业务层,可以通过 ghttp.Request
对象的以下方法获取:
func (r *Request) GetRouterValue(key string, def ...interface{}) interface{}
func (r *Request) GetRouterVar(key string, def ...interface{}) *gvar.Var
func (r *Request) GetRouterString(key string, def ...interface{}) string
也可以使用 ghttp.Request.Get*
方式进行获取。