日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

gin 项目结构_Go Web 框架 Gin 路由的学习

發(fā)布時(shí)間:2025/3/17 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 gin 项目结构_Go Web 框架 Gin 路由的学习 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Gin 是目前應(yīng)用比較廣泛的Golang web 框架。目前,Github Star 數(shù)已經(jīng)達(dá)到了3.8w. 框架的實(shí)現(xiàn)非常簡(jiǎn)單,可定制性非常強(qiáng),性能也比較好,深受golang開(kāi)發(fā)者的喜愛(ài)。Gin 提供了web開(kāi)發(fā)的一些基本功能。如路由,中間件,日志,參數(shù)獲取等,本文主要從源碼的角度分析Gin的路由實(shí)現(xiàn)。

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?engine
type?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é)

  • httprouter 沒(méi)有實(shí)現(xiàn)了routergroup功能,只是實(shí)現(xiàn)了router 的功能,在gin中做了實(shí)現(xiàn)
  • 通過(guò)Trie樹(shù)實(shí)現(xiàn)路由是比較基礎(chǔ)的一種實(shí)現(xiàn)方法,除了這種方法外,還可以考慮通過(guò)正則的方式提取路由。
  • Gin http 服務(wù)是基于 Go 的 net/http 庫(kù)的, net/http 庫(kù)中handler 的實(shí)現(xiàn)是針對(duì)不同的 http method 的,所以需要在engine 中針對(duì)不同的method 提供不同的trie 樹(shù)。
  • 在添加路由時(shí),如果使用了 any 方法,則在每個(gè)http method 下都會(huì)添加一樣的路徑。
  • middleware 本質(zhì)上只是一個(gè) HandlerFunc.
  • 推薦閱讀

    • 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)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。