gin 项目结构_Go Web 框架 Gin 路由的学习
Gin 的路由功能是基于 https://github.com/julienschmidt/httprouter 這個(gè)項(xiàng)目實(shí)現(xiàn)的。目前也有很多其他Web框架也基于該路由框架做了二次開(kāi)發(fā)。
http 路由的接口
在 Gin 中,為了兼容不同路由的引擎,定義了 IRoutes 和 IRouter 接口,便于替換其他的路由實(shí)現(xiàn)。(目前默認(rèn)是httprouter)
下面是一個(gè)路由的接口定義:
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
}
type?HandlerFunc?func(*Context)
HandlerFunc 是一個(gè)方法類(lèi)型的定義,我們定義的路由其實(shí)就是一個(gè)路徑與HandlerFunc 的映射關(guān)系。從上面的定義可以看出,IRoutes 主要定義了一些基于http方法、靜態(tài)方法的路徑和一組方法的映射。Use 方法是針對(duì)此路由的所有路徑映射一組方法,在使用上是為了給這些路由添加中間件。
除了上面的定義外,Gin 還有路由組的抽象。
type?IRouter?interface?{??IRoutes
??Group(string,?...HandlerFunc)?*RouterGroup
}
路由組是在IRoutes 的基礎(chǔ)上,有了組的概念,組下面還可以?huà)煸诓煌慕M。組的概念可以很好的管理一組路由,路由組可以自己定義一套Handler方法(即一組中間件)。
個(gè)人認(rèn)為IRouter的定義Group 應(yīng)該返回 IRouter,這樣可以把路由組更加抽象,也不會(huì)改變現(xiàn)有服務(wù)的使用。期待看下Gin源碼什么時(shí)候會(huì)按照這種定義方法修改過(guò)來(lái)。
在Gin框架中,路由由 RouterGroup 實(shí)現(xiàn)。我們從構(gòu)造和路由查找兩個(gè)方面分析路由的實(shí)現(xiàn)。
路由實(shí)現(xiàn)
路由的本質(zhì)就是在給定 路徑與Handler映射關(guān)系 的前提下,當(dāng)提供新的url時(shí),給出對(duì)應(yīng)func 的過(guò)程。其中可能需要從url中提取參數(shù),或者按照 * 匹配 url 的情況。
首先,我們看下Gin中路由結(jié)構(gòu)的定義。
//?gin?enginetype?Engine?struct?{
??RouterGroup
??//?...?其他字段
??trees????????????methodTrees
}
//?每個(gè)?http?方法定義一個(gè)森林
type?methodTrees?[]methodTree
type?methodTree?struct?{
??method?string
??root???*node
}
//?路由組的定義
type?RouterGroup?struct?{
??Handlers?HandlersChain
??basePath?string
??engine???*Engine
??root?????bool
}
從定義中可以看出,其實(shí)Gin 的 Engine 是復(fù)用了 RouterGroup。對(duì)于不同的 http method,都通過(guò)一個(gè)森林來(lái)存儲(chǔ)路由數(shù)據(jù)。下面是森林上每個(gè)節(jié)點(diǎn)的定義:
type?node?struct?{??path??????string??//?當(dāng)前路徑
??indices???string??//?對(duì)應(yīng)children?的前綴
??wildChild?bool???//?可能是帶參數(shù)的,或者是?*?的,所以是野節(jié)點(diǎn)
??nType?????nodeType??//?參數(shù)節(jié)點(diǎn),靜態(tài)節(jié)點(diǎn)
??priority??uint32??//?優(yōu)先級(jí)?,優(yōu)先級(jí)高的放在children 放在前面。
??children??[]*node??//?子節(jié)點(diǎn)
??handlers??HandlersChain?//?調(diào)用鏈
??fullPath??string??//?全路徑
}
從代碼實(shí)現(xiàn)上得知,這個(gè)森林其實(shí)是一個(gè)壓縮版本的Trie樹(shù),每個(gè)節(jié)點(diǎn)會(huì)存儲(chǔ)前綴相同的路徑數(shù)據(jù)。下面,我們通過(guò)代碼來(lái)學(xué)習(xí)下路由的添加和刪除。
路由的添加
路由的添加,就是將path路徑添加到定義的Trie樹(shù)種,將handlers 添加到對(duì)應(yīng)的node 節(jié)點(diǎn)。
func?(n?*node)?addRoute(path?string,?handlers?HandlersChain)?{??//?初始化和維護(hù)優(yōu)先級(jí)
??for?{
????//?查找前綴
????i?:=?longestCommonPrefix(path,?n.path)
?//?原有路徑長(zhǎng)的情況下
?//?節(jié)點(diǎn)n?的?path?變?yōu)榱斯睬熬Y
?//?原有n?的path?路徑變?yōu)榱爽F(xiàn)有n?的子節(jié)點(diǎn)
?//?當(dāng)添加的path長(zhǎng)的情況
?//?需要分情況討論:
?//?1.?如果是一個(gè)帶參數(shù)的路徑,校驗(yàn)是否后續(xù)路徑不同,如果不同則繼續(xù)掃描下一段路徑
?//?2.?如果是帶?*?的路徑,?則直接報(bào)錯(cuò)
?//?3.?如果已經(jīng)有對(duì)應(yīng)的首字母,修改當(dāng)前node節(jié)點(diǎn),并繼續(xù)掃描,并掃描下一段路徑
?//?4.?如果非參數(shù)或者?*?匹配的方法,則插入一個(gè)子節(jié)點(diǎn)路徑,并完成掃描
?//?最后注冊(cè)handlers,添加fullPath
????n.handlers?=?handlers
????n.fullPath?=?fullPath
????return
??}
}
從上面的代碼注釋可以看出,路由的添加,主要是通過(guò)不斷對(duì)比當(dāng)前節(jié)點(diǎn)的path和添加的path,做添加節(jié)點(diǎn)或者節(jié)點(diǎn)變更的操作,達(dá)到添加path的目的。
路徑查找
在服務(wù)請(qǐng)求時(shí),路由的責(zé)任就是給定一個(gè)url請(qǐng)求,拿到節(jié)點(diǎn)保存的handlers,以及url中包含的參數(shù)值。下面是對(duì)一個(gè)url 的解析實(shí)現(xiàn)。
type?nodeValue?struct?{?handlers?HandlersChain
?params???*Params
?tsr??????bool
?fullPath?string
}
func?(n?*node)?getValue(path?string,?params?*Params,?unescape?bool)?(value?nodeValue)?{
walk:?//?Outer?loop?for?walking?the?tree
??for?{
????prefix?:=?n.path
//?如果比當(dāng)前節(jié)點(diǎn)路徑要長(zhǎng):
//??-?非參數(shù)類(lèi)型或模糊匹配的URL,如果和當(dāng)前節(jié)點(diǎn)前綴匹配,直接查看?node?的子節(jié)點(diǎn)
//??-?參數(shù)化的node, 按照?/?分割提取參數(shù),如果未結(jié)束,則繼續(xù)匹配剩下的路徑,否則返回結(jié)果。
//??-?*?匹配的node,將剩余的路徑添加到 param 中直接返回。
//?如果和當(dāng)前節(jié)點(diǎn)相等,那就直接返回即可。
//?這里還做了非本方法的路徑匹配,用戶(hù)返回http 方法錯(cuò)誤的異常報(bào)告。
??}
}
一個(gè)例子
下面通過(guò)一個(gè)例子,方便我們快速理解router的實(shí)現(xiàn)。
加入下面的一個(gè)路徑:
/search/?
/support/?
/blog/:post/?
/about-us/team/?
/contact/
在樹(shù)中,我們看到的樣子如下:
??Path??\
??├s
??|├earch\
??|└upport\
??├blog\
??|????└:post
??|?????????└\
??├about-us\
??|????????└team\
??└contact\
在做路由查找時(shí),通過(guò)路徑不斷匹配,找到對(duì)應(yīng)的子節(jié)點(diǎn)。拿到對(duì)應(yīng)子節(jié)點(diǎn)下的handler。完成路由的匹配。
總結(jié)
推薦閱讀
Gin 框架路由拆分與注冊(cè)
喜歡本文的朋友,歡迎關(guān)注“Go語(yǔ)言中文網(wǎng)”:
Go語(yǔ)言中文網(wǎng)啟用微信學(xué)習(xí)交流群,歡迎加微信:274768166,投稿亦歡迎
總結(jié)
以上是生活随笔為你收集整理的gin 项目结构_Go Web 框架 Gin 路由的学习的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: OJ1052: 数列求和4(C语言)
- 下一篇: 3t硬盘坏道检测需要多久_卤素检测报告需