Beego AutoRouter工作原理解析
一、前言 🎈
Beego Web框架應該是國內Go語言社區第一個框架,個人覺得十分適合新手入門Go Web。筆者半年前寫過一篇搭建Beego項目並實習簡單功能的文章,大傢有興趣可以先看看。
其實我接觸的大部分人都在學校學過Java Web,其實有Java Web的經驗,上手Beego也會很舒服。
本文著重講講Beego的AutoRouter模塊,會結合源碼來講講,不過由於筆者技術水平有限,如有錯誤,煩請指出。🎉
二、從一個例子入手✨
Beego的路由設計靈感是sinatra,剛開始並不支持自動路由,項目的每一個路由都需要開發者配置。
🎃 不過,在Beego裡面註冊一個路由是十分簡單的,不信你看:
import "github.com/beego/beego/v2/server/web" type ReganYueController struct { web.Controller }
接下來我們可以添加一個方法,也可以重寫Get,Post,Delete等方法來響應客戶端不同的請求方式。
import "github.com/beego/beego/v2/server/web" type ReganYueController struct { web.Controller } func (u *ReganYueController) HelloWorld() { u.Ctx.WriteString("Welcome, Regan Yue") } func main() { web.AutoRouter(&ReganYueController{}) web.Run() }
該處web.AutoRouter(&ReganYueController{})
就是使用的自動路由,如果是以前的話,我們還需要配置路由🥶 。例如以下這種形式:
beego.Router("/", &IndexController{})
對於下面這段代碼,有幾點需要註意:
func (u *ReganYueController) HelloWorld() { u.Ctx.WriteString("Welcome, Regan Yue") }
⏰這個處理HTTP請求的方法必須是公共方法(首字母要大寫),並且不能有參數,不能有返回值,若非如此,可能會發生Panic。
🎨AutoRouter的解析規則:
影響因素有三:
RouterCaseSensitive
的值。Controller
的名字- 方法名字
比如我們上面ReganYueController的名字是ReganYue,而方法名字是HelloWorld,那麼就會有以下幾種情況出現:
- 如果
RouterCaseSensitive
為true
,那麼AutoRouter就會註冊兩個路由,其中一個是/ReganYue/HelloWorld/*
,另一個是/reganyue/helloworld/*
。 - 如果
RouterCaseSensitive
為false
,那麼AutoRouter隻會註冊一個路由,即/reganyue/helloworld/*
。
三、AutoRouter是如何工作的
先看看web.AutoRouter()
// AutoRouter see HttpServer.AutoRouter func AutoRouter(c ControllerInterface) *HttpServer { return BeeApp.AutoRouter(c) }
web.AutoRouter()
馬上又指向(app *HttpServer) AutoRouter(c ControllerInterface)
// AutoRouter adds defined controller handler to BeeApp. // it's same to HttpServer.AutoRouter. // if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page, // visit the url /main/list to exec List function or /main/page to exec Page function. func (app *HttpServer) AutoRouter(c ControllerInterface) *HttpServer { app.Handlers.AddAuto(c) return app }
前面傳來的主語BeeApp
執行該處程序:
BeeApp是一個應用實例,使用NewHttpSever()創建,繼續跟進,發現是根據Bconfig
這個配置文件創建的,
// NewHttpServerWithCfg will create an sever with specific cfg func NewHttpServerWithCfg(cfg *Config) *HttpServer { cr := NewControllerRegisterWithCfg(cfg) app := &HttpServer{ Handlers: cr, Server: &http.Server{}, Cfg: cfg, } return app }
上圖即配置Bconfig的主要結構。
到此我們對於BeeApp已經有一定瞭解瞭,下面我們回過頭來看看app.Handlers.AddAuto(c)
。
先看看這個c
是什麼,它的類型是ControllerInterface
,我們現在進去看看。
這個c是用來統一所有controller handler的接口。
根據上圖我們可以知道,這個app.Handles就是ControllerRegister,再來看看ControllerRegister的AddAuto方法:
func (p *ControllerRegister) AddAuto(c ControllerInterface) { p.AddAutoPrefix("/", c) }
AddAuto又指向AddAutoPrefix,這個AddAutoPrefix有什麼用,我們先給出一個例子,然後再來看源碼。
beego.AddAutoPrefix("/admin",&MainContorlller{})
如果MainContorlller
有兩個方法List
、Page
。那麼我們可以訪問/admin/main/list
來執行List
函數,訪問/admin/main/page
來執行Page
函數
來看看ControllerRegister的AddAutoPrefix方法:
func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) { //對傳入的Controller做反射 reflectVal := reflect.ValueOf(c) //獲取傳入的Controller的類型 rt := reflectVal.Type() //因為c是指針,所以要用Indirect方法獲取指針指向的變量類型 ct := reflect.Indirect(reflectVal).Type() //使用Beego註冊controller的名稱後面有Controller,這裡把它去掉得到controllerName。 controllerName := strings.TrimSuffix(ct.Name(), "Controller") // for i := 0; i < rt.NumMethod(); i++ { if !utils.InSlice(rt.Method(i).Name, exceptMethod) { route := &ControllerInfo{} route.routerType = routerTypeBeego route.methods = map[string]string{"*": rt.Method(i).Name} route.controllerType = ct pattern := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name), "*") patternInit := path.Join(prefix, controllerName, rt.Method(i).Name, "*") patternFix := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name)) patternFixInit := path.Join(prefix, controllerName, rt.Method(i).Name) route.pattern = pattern for m := range HTTPMETHOD { p.addToRouter(m, pattern, route) p.addToRouter(m, patternInit, route) p.addToRouter(m, patternFix, route) p.addToRouter(m, patternFixInit, route) } } } }
reflectVal.Type()
直接的獲取傳入的Controller的類型,而reflect.Indirect(reflectVal).Type()
,interface其實就是兩個指針,一個指向類型信息,一個指向實際的對象,用Indirect方法獲取指針指向的實際變量的類型。
在runtime/runtime2.go
可以瞭解interface其實就是兩個指針:
type iface struct { tab *itab //類型信息 data unsafe.Pointer //實際對象指針 } type itab struct { inter *interfacetype //接口類型 _type *_type //實際對象類型 hash uint32 _ [4]byte fun [1]uintptr //實際對象方法地址 }
接下來是for i := 0; i < rt.NumMethod(); i++
,我們來看看這個NumMethod()
,可以看到這個方法獲得interface類型的方法數量。
utils.InSlice()方法正如其名:
func InSlice(v string, sl []string) bool { for _, vv := range sl { if vv == v { return true } } return false }
該方法是用來判斷字符串v是不是在字符串切片sl裡面。
此處判斷方法名是不是在exceptMethod裡面。
下面是exceptMethod的內容:
exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString", "RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJSON", "ServeJSONP", "ServeYAML", "ServeXML", "Input", "ParseForm", "GetString", "GetStrings", "GetInt", "GetBool", "GetFloat", "GetFile", "SaveToFile", "StartSession", "SetSession", "GetSession", "DelSession", "SessionRegenerateID", "DestroySession", "IsAjax", "GetSecureCookie", "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml", "GetControllerAndAction", "ServeFormatted"}
接下來創建瞭一個結構體,記錄瞭controller的信息,下面幾行代碼就生成瞭每個方法對應的controller信息。
controller的pattern這裡生成瞭4個模式:
- prefix/全小寫的controllerName/全小寫的方法名/*
- prefix/controllerName/方法名/*
- prefix/全小寫的controllerName/全小寫的方法名
- prefix/controllerName/方法名
然後對每一種HTTP方法:
都使用addToRouter
方法用四種模式執行一遍。
下面看看addToRouter。
func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) { if !p.cfg.RouterCaseSensitive { pattern = strings.ToLower(pattern) } if t, ok := p.routers[method]; ok { t.AddRouter(pattern, r) } else { t := NewTree() t.AddRouter(pattern, r) p.routers[method] = t } }
- 如果
RouterCaseSensitive
為true
,那麼AutoRouter就會註冊兩個路由,其中一個是/ReganYue/HelloWorld/*
,另一個是/reganyue/helloworld/*
。 - 如果
RouterCaseSensitive
為false
,那麼AutoRouter隻會註冊一個路由,即/reganyue/helloworld/*
。
然後將method傳給ControllerRegister,看是不是註冊成功。
成功就執行:t.AddRouter(pattern, r)
添加路由。
否則就執行:
t := NewTree() t.AddRouter(pattern, r) p.routers[method] = t
那就到此為止吧,
再愛就不禮貌瞭…
結語
本文通過源碼解析,從一個例子入手,瞭解Beego的AutoRouter模塊是如何工作的,以上就是Beego AutoRouter工作原理解析的詳細內容,更多關於Beego AutoRouter工作原理的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Go web中cookie值安全securecookie庫使用原理
- Python Flask入門
- Go語言字符串基礎示例詳解
- 詳解如何在ASP.NET Core中使用Route特性
- SpringMVC執行步驟、Model的使用詳解