瞭解beego的開發者確定知道,beego的路由設計來源於sinatra,原來是不支持自動路由的,每個路由都要本身配置的,如:php
type MainController struct { beego.Controller } func (this *MainController) Get() { this.Ctx.WriteString("hello world") } func main() { beego.Router("/", &MainController{}) beego.Run() }
beego.Controller 提供全部的restful方法,Get,Post,Delete等方法,經過重寫這些方法,已響應客戶端不一樣的請求方式。java
用過Node.js的同窗,確定以爲很熟悉,拿最經常使用的express框架來講,路由的定義方式也大抵是這樣的,如:git
app.get('/', index); var index = function(req,res){ res.send("Hello,express"); };
有的同窗確定以爲爲何像php,java,asp這種服務器語言就不須要這樣定義路由,天然會根據請求url,判斷腳本path,進項執行並返回。github
其實這些也是須要的,只不過這個工做交給了服務器軟件來解決,如Apache,Nginx,IIS,Tomcat等。由這些軟件提供Http服務,腳本程序自己更專一於服務的邏輯。golang
而如Node.js,Golang這種語言,是由自生提供Http服務,並監聽某一端口。因此經過查看服務器響應Header能夠看出,此類語言的server顯示爲express,beegoserver,而大部分網站返回頭的server爲Nginx,Apache等。固然,Golang,Node.js也能夠經過反向代理功能(如Nginx),使真正與客戶端打交道的變爲這些反向代理軟件,但注意的是,這並不表明Node.js等的Http服務和路由調度不工做了,他們依然接受來自反向代理軟件的Http請求,並做出響應。express
好了,扯了這麼多,那0.9.0版本的beego提供的智能路由到底是怎樣呢?服務器
先看一段,示例代碼:restful
package main import ( "github.com/astaxie/beego"
"myapp/beego/controllers"
) func main() { beego.AutoRouter(&controllers.UserController{}) beego.AutoRouter(&controllers.PageController{}) //........ beego.Run() }
控制器代碼以下:cookie
package controllers import ( "github.com/astaxie/beego" ) type UserController struct { beego.Controller } type PageController struct {
beego.Controller
} func (this *UserController) Add() { this.Ctx.WriteString("/user/add") }
func (this *PageController) About() {
this.Ctx.WriteString("/page/about")
}
有了這個AutoRouter,便不須要像之前那樣逐一註冊了,訪問/user/add 調用UserController的Add方法,訪問/page/about調用PageController的About方法。app
這裏須要稍微提醒兩點:
1.控制器struct極其下func都必須以大寫字母開頭,由於在Golang裏默認大寫開頭的爲public,小寫開頭的爲private,私有的內容沒法被包外訪問。
2.在使用了AutoRouter以後,原來的Router方法依然是有效的,能夠繼續使用。
好了,AutoRouter的使用就先介紹這裏,0.9.0版本的beego仍是更新和添加了很多功能的,在這裏感謝Astaxie爲golang項目所作的努力。
beego具體全面的使用,你們若是感興趣的話,我之後能夠抽個時間作個完成的介紹。
接下來咱們來具體看看AutoRouter是怎麼工做的,源碼走起:
func AutoRouter(c ControllerInterface) *App { BeeApp.AutoRouter(c) return BeeApp }
func (app *App) AutoRouter(c ControllerInterface) *App { app.Handlers.AddAuto(c) return app }
type App struct { Handlers *ControllerRegistor }
func (p *ControllerRegistor) AddAuto(c ControllerInterface) { p.enableAuto = true reflectVal := reflect.ValueOf(c) rt := reflectVal.Type() ct := reflect.Indirect(reflectVal).Type() firstParam := strings.ToLower(strings.TrimSuffix(ct.Name(), "Controller")) if _, ok := p.autoRouter[firstParam]; ok { return } else { p.autoRouter[firstParam] = make(map[string]reflect.Type) } for i := 0; i < rt.NumMethod(); i++ { p.autoRouter[firstParam][rt.Method(i).Name] = ct } }
type ControllerRegistor struct { routers []*controllerInfo fixrouters []*controllerInfo enableFilter bool filters []http.HandlerFunc enableAfter bool afterFilters []http.HandlerFunc enableUser bool userHandlers map[string]*userHandler enableAuto bool autoRouter map[string]map[string]reflect.Type //key:controller key:method value:reflect.type }
if p.enableAuto { if !findrouter { for cName, methodmap := range p.autoRouter { if strings.ToLower(requestPath) == "/"+cName { http.Redirect(w, r, requestPath+"/", 301) return } if strings.ToLower(requestPath) == "/"+cName+"/" { requestPath = requestPath + "index" } if strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/") { for mName, controllerType := range methodmap { if strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/"+strings.ToLower(mName)) { //execute middleware filters if p.enableFilter { for _, filter := range p.filters { filter(w, r) if w.started { return } } } //parse params otherurl := requestPath[len("/"+cName+"/"+strings.ToLower(mName)):] if len(otherurl) > 1 { plist := strings.Split(otherurl, "/") for k, v := range plist[1:] { params[strconv.Itoa(k)] = v } } //Invoke the request handler vc := reflect.New(controllerType) //call the controller init function init := vc.MethodByName("Init") in := make([]reflect.Value, 2) ct := &Context{ResponseWriter: w, Request: r, Params: params, RequestBody: requestbody} in[0] = reflect.ValueOf(ct) in[1] = reflect.ValueOf(controllerType.Name()) init.Call(in) //call prepare function in = make([]reflect.Value, 0) method := vc.MethodByName("Prepare") method.Call(in) method = vc.MethodByName(mName) method.Call(in) //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf if EnableXSRF { method = vc.MethodByName("XsrfToken") method.Call(in) if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || (r.Method == "POST" && (r.Form.Get("_method") == "delete" || r.Form.Get("_method") == "put")) { method = vc.MethodByName("CheckXsrfCookie") method.Call(in) } } if !w.started { if AutoRender { method = vc.MethodByName("Render") method.Call(in) } } method = vc.MethodByName("Finish") method.Call(in) //execute middleware filters if p.enableAfter { for _, filter := range p.afterFilters { filter(w, r) if w.started { return } } } method = vc.MethodByName("Destructor") method.Call(in) // set find findrouter = true } } } } } }
這裏能夠看到最早有一個判斷if !findrouter 即若是沒有找到路由匹配,纔會進行智能路由匹配,因此Router的優先級是比AutoRouter要高的。
在這裏再次用到了reflect,這裏 ct := &Context{ResponseWriter: w, Request: r, Params: params, RequestBody: requestbody} 即獲取http請求上下文,經過method.Call(in),
將http請求參數傳給Controller內的相對應的方法。
不難看出,作了多步處理,有點相似PHP的鉤子(hooks),依次通過控制器init方法->Prepare->XsrfToken->Render->Finish->Destructor等處理。
在最後set findrouter 爲true,若是在這裏仍沒有匹配到router,接下來就404了
if !findrouter { if h, ok := ErrorMaps["404"]; ok { w.status = 404 h(w, r) } else { http.NotFound(w, r) } }
因此beego的設計仍是比較嚴謹且有效率的,在這裏在此表明廣大Golang初學者感謝謝大。
額,第一次寫Golang的文章,感受力不從心,說了一堆廢話,忘園友們見諒!