之前,我們用了乙個非常簡單的map結構儲存了路由表,使用map儲存鍵值對,索引非常高效,但是有乙個弊端,鍵值對的儲存的方式,只能用來索引靜態路由。那如果我們想支援類似於/hello/:name這樣的動態路由怎麼辦呢?所謂動態路由,即一條路由規則可以匹配某一型別而非某一條固定的路由。例如/hello/:name,可以匹配/hello/geektutu、hello/jack等。
//字首樹每個節點的屬性
type node struct
//一.返回第乙個匹配成功的子節點child,part是與該child匹配的部分路由,用於插入字首樹
func
(n *node)
matchchild
(part string
)*node
}return
nil}
//二.為了實現動態路由匹配,加上了iswild這個引數。即當我們匹配 /p/go/doc/這個路由時,
//第一層節點,p精準匹配到了p,第二層節點,go模糊匹配到:lang,那麼將會把lang這個引數賦值為go,繼續下一層匹配。
//返回所有與part匹配成功的子節點,用於查詢,可以是*和:這倆萬用字元,也可以是與part相等的路由
func
(n *node)
matchchildern
(part string)[
]*node
}return nodes
}
//三.trier樹的插入或構建:
//遞迴查詢每一層的節點,如果沒有匹配到當前part的節點,則新建乙個,有一點需要注意,/p/:lang/doc只有在第三層節點,
//即doc節點,pattern才會設定為/p/:lang/doc。p和:lang節點的pattern屬性皆為空。因此,當匹配結束時,我們可以
//使用n.pattern == ""來判斷路由規則是否匹配成功。例如,/p/python雖能成功匹配到:lang,但:lang的pattern值為空,因此匹配失敗
func
(n *node)
insert
(pattern string
, parts [
]string
, height int
) part := parts[height]
child := n.
matchchild
(part)
if child ==
nil n.children =
(n.children, child)
} child.
insert
(pattern, parts, height+1)
}//四.trier樹的查詢:
//最終返回的是路由中的最後乙個節點值*node,此時pattern屬性不為空,為完整路由
func
(n *node)
search
(parts [
]string
, height int
)*node
return n//這個n最後會變成最後乙個子節點
} part := parts[height]
children := n.
matchchildren
(part)
for_
,child :=
range children
}return
nil}
//五.使用 roots 來儲存每種請求方式的trie 樹根節點。使用 handlers 儲存每種請求方式的 handlerfunc
type router struct
// roots key eg, roots['get'] roots['post']
// handlers key eg, handlers['get-/p/:lang/doc'], handlers['post-/p/book']
func
newrouter()
*router }
handlers:
make
(map
[string
]handlerfunc),//
}}//解析路由,新增進slice
func
parsepattern
(pattern string)[
]string}}
return parts
}//新增各種路由構建trier樹
func
(r *router)
addroute
(method string
, pattern string
, handler handlerfunc)
//新建乙個method的trier樹
} r.roots[method]
.insert
(pattern, parts,0)
//每個method都構建一顆trier樹
r.handlers[key]
= handler
}//六.getroute 函式中,還解析了:和*兩種匹配符的引數,返回乙個 map 。例如/p/go/doc匹配到/p/:lang/doc,解析結果為:
func
(r *router)
getroute
(method string
, path string)(
*node,
map[
string
]string
) n := root.
search
(searchparts,0)
if n !=
nilif part[0]
=='*'
&&len
(part)
>1}
return n, params
}return
nil,
nil}
//單元測試
func
newtestrouter()
*router
func
testparsepattern
(t *testing.t)
) ok = ok && reflect.
deepequal
(parsepattern
("/p/*"),
string
) ok = ok && reflect.
deepequal
(parsepattern
("/p/*name/*"),
string)if
!ok
}func
testgetroute
(t *testing.t)
if n.pattern !=
"/hello/:name"
if ps[
"name"]!=
"geektutu"
fmt.
printf
("matched path: %s, params['name']: %s\n"
, n.pattern, ps[
"name"])
}//demo
func
main()
) r.
get(
"/hello"
,func
(c *gee.context)
) r.
get(
"/hello/:name"
,func
(c *gee.context)
) r.
get(
"/assets/*filepath"
,func
(c *gee.context))}
) r.
run(
":9999"
)}
web開發框架Flask學習二
jinja2模板規範 在當前專案中建立乙個檔案為templates的資料夾,將其設定為模板資料夾,新建的html為模板頁面,在檢視函式中使用render template html的檔案 my list mylist my list作為在模板頁面使用的的變數 過濾器 符號 管道左邊作為輸入傳乙個變數...
(二)flask框架使用教程系列 理解web
url是 uniform resource locator 的簡寫,統一資源定位符。乙個 url 由一下幾部分組成 scheme host port path query string anchor 2.1 如果使用的是http 協議,那麼瀏覽器就會使用80埠去請求這個伺服器的資源,例如 2.2如果...
仿Gin搭建自己的web框架 二
本文介紹gin的路由控制。複製 這條語句通過http.handlefunc函式註冊了對路徑 處理的函式handler。看一下它內部的實現 func handlefunc pattern string,handler func responsewriter,request 複製 可以看到是呼叫了 de...