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