W3Cschool
恭喜您成為首批注冊用戶
獲得88經驗值獎勵
?goframe
?框架自建了非常強大的路由功能,提供了比任何同類框架更加出色的路由特性,支持流行的命名匹配規(guī)則、模糊匹配規(guī)則及字段匹配規(guī)則,并提供了優(yōu)秀的優(yōu)先級管理機制。
在真正開啟本章的核心內容之前,我們先來看一個簡單的動態(tài)路由使用示例:
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
?框架支持的三種模糊匹配路由規(guī)則,?:name
?、?*any
?、?{field}
?分別表示命名匹配規(guī)則、模糊匹配規(guī)則及字段匹配規(guī)則。不同的規(guī)則中使用?/
?符號來劃分層級,路由檢索采用深度優(yōu)先算法,層級越深的規(guī)則優(yōu)先級也會越高。我們運行以上示例,通過訪問幾個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
在這個示例中我們也可以看到,由于優(yōu)先級的限制,路由規(guī)則?/:name
?會被?/:name/*any
?規(guī)則覆蓋,將會無法被匹配到,所以在分配路由規(guī)則的時候,需要進行統(tǒng)一規(guī)劃和管理,避免類似情況的產生。
我們來看一下之前一直使用的?BindHandler
?的原型:
func (s *Server) BindHandler(pattern string, handler interface{})
該方法是路由注冊的最基礎方法,其中的?pattern
?為路由注冊規(guī)則字符串,在其他路由注冊方法中也會使用到,參數格式如下:
[HTTPMethod:]路由規(guī)則[@域名]
其中?HTTPMethod
?(支持的Method:?GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE
?)和?@域名
?為非必需參數,一般來說直接給定路由規(guī)則參數即可,?BindHandler
?會自動綁定所有的請求方式,如果給定?HTTPMethod
?,那么路由規(guī)則僅會在該請求方式下有效。?@域名
?可以指定生效的域名名稱,那么該路由規(guī)則僅會在該域名下生效。
?BindHandler
?是最原生的路由注冊方法,在大部分場景中,我們通常使用分組路由方式來管理理由。
我們來看一個例子:
package main
import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/frame/g"
)
func main() {
s := g.Server()
// 該路由規(guī)則僅會在GET請求下有效
s.BindHandler("GET:/{table}/list/{page}.html", func(r *ghttp.Request){
r.Response.WriteJson(r.Router)
})
// 該路由規(guī)則僅會在GET請求及l(fā)ocalhost域名下有效
s.BindHandler("GET:/order/info/{order_id}@localhost", func(r *ghttp.Request){
r.Response.WriteJson(r.Router)
})
// 該路由規(guī)則僅會在DELETE請求下有效
s.BindHandler("DELETE:/comment/{id}", func(r *ghttp.Request){
r.Response.WriteJson(r.Router)
})
s.SetPort(8199)
s.Run()
}
其中返回的參數?r.Router
?是當前匹配的路由規(guī)則信息,訪問當該方法的時候,服務端會輸出當前匹配的路由規(guī)則信息。執(zhí)行后,我們在終端使用?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
值得說明的是,在大多數場景下,我們很少直接在路由規(guī)則中使用?@域名
?這樣的規(guī)則來限定路由注冊的域名,而是使用?ghttp.Server.Domain(domains string)
?方法來獲得指定域名列表的管理對象,隨后使用該域名對象進行路由注冊,域名對象即可實現對指定域名的綁定操作。
精準匹配規(guī)則即未使用任何動態(tài)規(guī)則的規(guī)則,如:?user
?、?order
?、?info
?等等這種確定名稱的規(guī)則。在大多數場景下,精準匹配規(guī)則會和動態(tài)規(guī)則一起使用來進行路由注冊(例如:?/:name/list
?,其中層級1?:name
?為命名匹配規(guī)則,層級2?list
?是精準匹配規(guī)則)。
動態(tài)路由規(guī)則分為三種:命名匹配規(guī)則、模糊匹配規(guī)則和字段匹配規(guī)則。動態(tài)路由的底層數據結構是由層級哈希表和雙向鏈表構建的路由樹,層級哈希表便于高效率地層級匹配?URI
?;數據鏈表用于優(yōu)先級控制,同一層級的路由規(guī)則按照優(yōu)先級進行排序,優(yōu)先級高的規(guī)則排在鏈表頭。底層的路由規(guī)則與請求?URI
?的匹配計算采用的是正則表達式,并充分使用了緩存機制,執(zhí)行效率十分高效。
所有匹配到的參數都將會以?Router
?參數的形式傳遞給業(yè)務層,可以通過?ghttp.Request
?對象的以下方法獲?。?/p>
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*
?方式進行獲取。
使用?:name
?方式進行匹配(?name
?為自定義的匹配名稱),對?URI
?指定層級的參數進行命名匹配(類似正則([^/]+),該URI層級必須有值),對應匹配參數會被解析為Router參數并傳遞給注冊的服務接口使用。
匹配示例1:
rule: /user/:user
/user/john match
/user/you match
/user/john/profile no match
/user/ no match
匹配示例2:
rule: /:name/action
/john/name no match
/john/action match
/smith/info no match
/smith/info/age no match
/smith/action match
匹配示例3:
rule: /:name/:action
/john/name match
/john/info match
/smith/info match
/smith/info/age no match
/smith/action/del no match
使用?*any
?方式進行匹配(?any
?為自定義的匹配名稱),對?URI
?指定位置之后的參數進行模糊匹配(類似正則?(.*)
?,該?URI
?層級可以為空),并將匹配參數解析為?Router
?參數并傳遞給注冊的服務接口使用。
匹配示例1:
rule: /src/*path
/src/ match
/src/somefile.go match
/src/subdir/somefile.go match
/user/ no match
/user/john no match
匹配示例2:
rule: /src/*path/:action
/src/ no match
/src/somefile.go match
/src/somefile.go/del match
/src/subdir/file.go/del match
匹配示例3:
rule: /src/*path/show
/src/ no match
/src/somefile.go no match
/src/somefile.go/del no match
/src/somefile.go/show match
/src/subdir/file.go/show match
/src/show match
使用?{field}
?方式進行匹配(?field
?為自定義的匹配名稱),可對?URI
?任意位置的參數進行截取匹配(類似正則?([\w\.\-]+)
?,該?URI
?層級必須有值,并且可以在同一層級進行多個字段匹配),并將匹配參數解析為?Router
?參數并傳遞給注冊的服務接口使用。
匹配示例1:
rule: /order/list/{page}.php
/order/list/1.php match
/order/list/666.php match
/order/list/2.php5 no match
/order/list/1 no match
/order/list no match
匹配示例2:
rule: /db-{table}/{id}
/db-user/1 match
/db-user/2 match
/db/user/1 no match
/db-order/100 match
/database-order/100 no match
匹配示例3:
rule: /{obj}-{act}/*param
/user-delete/10 match
/order-update/20 match
/log-list match
/log/list/1 no match
/comment/delete/10 no match
package main
import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/frame/g"
)
func main() {
s := g.Server()
// 一個簡單的分頁路由示例
s.BindHandler("/user/list/{page}.html", func(r *ghttp.Request){
r.Response.Writeln(r.Get("page"))
})
// {xxx} 規(guī)則與 :xxx 規(guī)則混合使用
s.BindHandler("/{object}/:attr/{act}.php", func(r *ghttp.Request){
r.Response.Writeln(r.Get("object"))
r.Response.Writeln(r.Get("attr"))
r.Response.Writeln(r.Get("act"))
})
// 多種模糊匹配規(guī)則混合使用
s.BindHandler("/{class}-{course}/:name/*act", func(r *ghttp.Request){
r.Response.Writeln(r.Get("class"))
r.Response.Writeln(r.Get("course"))
r.Response.Writeln(r.Get("name"))
r.Response.Writeln(r.Get("act"))
})
s.SetPort(8199)
s.Run()
}
執(zhí)行后,我們可以通過?curl
?命令或者瀏覽器訪問的方式進行測試,以下為測試結果:
$ curl -XGET http://127.0.0.1:8199/user/list/1.html
1
$ curl -XGET http://127.0.0.1:8199/user/info/save.php
user
info
save
$ curl -XGET http://127.0.0.1:8199/class3-math/john/score
class3
math
john
score
優(yōu)先級控制按照深度優(yōu)先策略,主要的幾點因素:
我們來看示例(左邊的規(guī)則優(yōu)先級比右邊高):
/:name > /*any
/user/name > /user/:action
/:name/info > /:name/:action
/:name/:action > /:name/*action
/:name/{action} > /:name/:action
/src/path/del > /src/path
/src/path/del > /src/path/:action
/src/path/*any > /src/path
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯系方式:
更多建議: