一:首先man.go,整個程序的入口mysql
func main() {
beego.Run()
}
而後beego.run()代碼git
// Run beego application. // beego.Run() default run on HttpPort // beego.Run(":8089") // beego.Run("127.0.0.1:8089") func Run(params ...string) { if len(params) > 0 && params[0] != "" { strs := strings.Split(params[0], ":") if len(strs) > 0 && strs[0] != "" { HttpAddr = strs[0] } if len(strs) > 1 && strs[1] != "" { HttpPort, _ = strconv.Atoi(strs[1]) } } initBeforeHttpRun() if EnableAdmin { go beeAdminApp.Run() } BeeApp.Run() }
能夠看出來,beego.run()能夠帶參數。github
beego.run()在默認的主機、端口號上運行,beego.run(port)在給定的端口號、默認的主機上運行。beego.run(addr:post)在給定的主機和端口上運行。redis
下面看看initBeforeHttpRun()的代碼。sql
func initBeforeHttpRun() { // if AppConfigPath not In the conf/app.conf reParse config if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") { err := ParseConfig() if err != nil && AppConfigPath != filepath.Join(workPath, "conf", "app.conf") { // configuration is critical to app, panic here if parse failed panic(err) } } // do hooks function for _, hk := range hooks { err := hk() if err != nil { panic(err) } } if SessionOn { var err error sessionConfig := AppConfig.String("sessionConfig") if sessionConfig == "" { sessionConfig = `{"cookieName":"` + SessionName + `",` + `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + `"providerConfig":"` + SessionSavePath + `",` + `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + `"sessionIDHashFunc":"` + SessionHashFunc + `",` + `"sessionIDHashKey":"` + SessionHashKey + `",` + `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + `"domain":"` + SessionDomain + `",` + `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` } GlobalSessions, err = session.NewManager(SessionProvider, sessionConfig) if err != nil { panic(err) } go GlobalSessions.GC() } err := BuildTemplate(ViewsPath) if err != nil { if RunMode == "dev" { Warn(err) } } middleware.VERSION = VERSION middleware.AppName = AppName middleware.RegisterErrorHandler() if EnableDocs { Get("/docs", serverDocs) Get("/docs/*", serverDocs) } //init mime AddAPPStartHook(initMime) }
能夠看到首先拼湊出來的是conf配置文件的路勁,若是存在而後調用ParseConfig()解析conf。json
而後是cookie
for _, hk := range hooks { err := hk() if err != nil { panic(err) } }
hooks的定義session
type hookfunc func() error //hook function to run var hooks []hookfunc //hook function slice to store the hookfunc func init() { hooks = make([]hookfunc, 0) }
hooks是一個hookfun的切片,app
// The hookfunc will run in beego.Run() // such as sessionInit, middlerware start, buildtemplate, admin start func AddAPPStartHook(hf hookfunc) { hooks = append(hooks, hf) }
上面的代碼是在啓動的時候添加本身的方法hook。也就是hooks是在啓動以前,留給用戶初始化一些東西的時候。dom
if SessionOn { var err error sessionConfig := AppConfig.String("sessionConfig") if sessionConfig == "" { sessionConfig = `{"cookieName":"` + SessionName + `",` + `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + `"providerConfig":"` + SessionSavePath + `",` + `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + `"sessionIDHashFunc":"` + SessionHashFunc + `",` + `"sessionIDHashKey":"` + SessionHashKey + `",` + `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + `"domain":"` + SessionDomain + `",` + `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` } GlobalSessions, err = session.NewManager(SessionProvider, sessionConfig) if err != nil { panic(err) } go GlobalSessions.GC() } err := BuildTemplate(ViewsPath) if err != nil { if RunMode == "dev" { Warn(err) } }
下面就開始來解析conf文件。若是sessionConfig爲空,就使用默認的json數據。而後就開始根據提供的config配置文件建立一個sessionmanager對象
這是session.NewManager()方法的實現
// Create new Manager with provider name and json config string. // provider name: // 1. cookie // 2. file // 3. memory // 4. redis // 5. mysql // json config: // 1. is https default false // 2. hashfunc default sha1 // 3. hashkey default beegosessionkey // 4. maxage default is none func NewManager(provideName, config string) (*Manager, error) { provider, ok := provides[provideName] if !ok { return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName) } cf := new(managerConfig) cf.EnableSetCookie = true err := json.Unmarshal([]byte(config), cf) if err != nil { return nil, err } if cf.Maxlifetime == 0 { cf.Maxlifetime = cf.Gclifetime } err = provider.SessionInit(cf.Maxlifetime, cf.ProviderConfig) if err != nil { return nil, err } if cf.SessionIDHashFunc == "" { cf.SessionIDHashFunc = "sha1" } if cf.SessionIDHashKey == "" { cf.SessionIDHashKey = string(generateRandomKey(16)) } return &Manager{ provider, cf, }, nil }
能夠推測。session.NewManager(SessionProvider,sessionConfig)中SessionProvider是一個全局變量,使用的是在config.go中默認的SessionProvider = "memory",而後改方法返回的是一個Manager的指針對象,即*Manager,因此GlobalSessions是一個*Manager對象。
而後啓動一個攜程執行GC()方法。下面是GC的源碼
// Start session gc process. // it can do gc in times after gc lifetime. func (manager *Manager) GC() { manager.provider.SessionGC() time.AfterFunc(time.Duration(manager.config.Gclifetime)*time.Second, func() { manager.GC() }) }
因此上面的代碼是一個無限循環,每隔一段time。DUration以後執行GC().
err := BuildTemplate(ViewsPath) if err != nil { if RunMode == "dev" { Warn(err) } }
這裏就開始編譯模板了。
// build all template files in a directory. // it makes beego can render any template file in view directory. func BuildTemplate(dir string) error { if _, err := os.Stat(dir); err != nil { if os.IsNotExist(err) { return nil } else { return errors.New("dir open err") } } self := &templatefile{ root: dir, files: make(map[string][]string), } err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { return self.visit(path, f, err) }) if err != nil { fmt.Printf("filepath.Walk() returned %v\n", err) return err } for _, v := range self.files { for _, file := range v { t, err := getTemplate(self.root, file, v...) if err != nil { Trace("parse template err:", file, err) } else { BeeTemplates[file] = t } } } return nil }
首先判斷目錄是否存在。目錄ViewsPath在config中有初始化。而後初始化templatefile結構體,filepath.Walk()
走一邊目錄裏的文件,記錄在self.files
裏面。循環self.files
中的file
(map[dir][]file]),用getTemplate
獲取template.Template
實例,保存在beego.BeeTemplates
(map[string]template.Template)。
而後是
middleware.VERSION = VERSION middleware.AppName = AppName middleware.RegisterErrorHandler() if EnableDocs { Get("/docs", serverDocs) Get("/docs/*", serverDocs) } //init mime AddAPPStartHook(initMime)
middleware包括的是錯誤處理的功能。如NotFound()、Forbidden()、Errorhandler()等等處理。。
隨後的AddAPPStartHook(initMine)則是初始化全部的minetype類型的函數。
上面的代碼實在beego項目啓動前須要操做的好比初始化conf配置、編譯模板文件、註冊錯誤處理中間件、加載全部的mimetype類型、
而後繼續回到beego.run()代碼中間
if EnableAdmin { go beeAdminApp.Run() } BeeApp.Run()
很簡單。若是beego容許admin。則執行beeAdminApp。beeAdminApp也是一個*beego.adminApp
,負責系統監控、性能檢測、訪問統計和健康檢查等。而後豬線程運行BeeApp.Run()方法,開始執行beego。
下面看看beego.adminApp的代碼
// adminApp is an http.HandlerFunc map used as beeAdminApp. type adminApp struct { routers map[string]http.HandlerFunc } // Route adds http.HandlerFunc to adminApp with url pattern. func (admin *adminApp) Route(pattern string, f http.HandlerFunc) { admin.routers[pattern] = f } // Run adminApp http server. // Its addr is defined in configuration file as adminhttpaddr and adminhttpport. func (admin *adminApp) Run() { if len(toolbox.AdminTaskList) > 0 { toolbox.StartTask() } addr := AdminHttpAddr if AdminHttpPort != 0 { addr = fmt.Sprintf("%s:%d", AdminHttpAddr, AdminHttpPort) } for p, f := range admin.routers { http.Handle(p, f) } err := http.ListenAndServe(addr, nil) if err != nil { BeeLogger.Critical("Admin ListenAndServe: ", err) } }
// task interface
type Tasker interface {
GetStatus() string
Run() error
SetNext(time.Time)
GetNext() time.Time
SetPrev(time.Time)
GetPrev() time.Time
}
AdminTaskList map[string]Tasker
// start all tasks func StartTask() { isstart = true go run() } func run() { now := time.Now().Local() for _, t := range AdminTaskList { t.SetNext(now) } for { sortList := NewMapSorter(AdminTaskList) sortList.Sort() var effective time.Time if len(AdminTaskList) == 0 || sortList.Vals[0].GetNext().IsZero() { // If there are no entries yet, just sleep - it still handles new entries // and stop requests. effective = now.AddDate(10, 0, 0) } else { effective = sortList.Vals[0].GetNext() } select { case now = <-time.After(effective.Sub(now)): // Run every entry whose next time was this effective time. for _, e := range sortList.Vals { if e.GetNext() != effective { break } go e.Run() e.SetPrev(e.GetNext()) e.SetNext(effective) } continue case <-changed: continue case <-stop: return } } } // start all tasks func StopTask() { isstart = false stop <- true } // add task with name func AddTask(taskname string, t Tasker) { AdminTaskList[taskname] = t if isstart { changed <- true } } // add task with name func DeleteTask(taskname string) { delete(AdminTaskList, taskname) if isstart { changed <- true } }
adminApp結構體裏面只有map結構的router,toolbox.AdminTaskList是一個map類型的結構。若是AdminTaskList中間有Tasker。則開始執行StartTask(),並且,StartTask()方法中實現的是另外打開一個協程執行Run()方法。最後打開http.ListenAndServe()實現監聽。
下面是BeeApp.Run()
package beego import ( "fmt" "net" "net/http" "net/http/fcgi" "time" "github.com/astaxie/beego/context" ) // FilterFunc defines filter function type. type FilterFunc func(*context.Context) // App defines beego application with a new PatternServeMux. type App struct { Handlers *ControllerRegistor Server *http.Server } // NewApp returns a new beego application. func NewApp() *App { cr := NewControllerRegister() app := &App{Handlers: cr, Server: &http.Server{}} return app } // Run beego application. func (app *App) Run() { addr := HttpAddr if HttpPort != 0 { addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort) } BeeLogger.Info("Running on %s", addr) var ( err error l net.Listener ) endRunning := make(chan bool, 1) if UseFcgi { if HttpPort == 0 { l, err = net.Listen("unix", addr) } else { l, err = net.Listen("tcp", addr) } if err != nil { BeeLogger.Critical("Listen: ", err) } err = fcgi.Serve(l, app.Handlers) } else { app.Server.Addr = addr app.Server.Handler = app.Handlers app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second if EnableHttpTLS { go func() { time.Sleep(20 * time.Microsecond) if HttpsPort != 0 { app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) } err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) if err != nil { BeeLogger.Critical("ListenAndServeTLS: ", err) time.Sleep(100 * time.Microsecond) endRunning <- true } }() } if EnableHttpListen { go func() { app.Server.Addr = addr err := app.Server.ListenAndServe() if err != nil { BeeLogger.Critical("ListenAndServe: ", err) time.Sleep(100 * time.Microsecond) endRunning <- true } }() } } <-endRunning }
上面的代碼首先是獲取地址addr,而後執行fast-cgi,調用ListenAndServeTLS監聽cgi服務,後面的是Http服務,調用ListenAndServe()監聽http服務。
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() } // ListenAndServe listens on the TCP network address srv.Addr and then // calls Serve to handle requests on incoming connections. If // srv.Addr is blank, ":http" is used. func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) } // Serve accepts incoming connections on the Listener l, creating a // new service goroutine for each. The service goroutines read requests and // then call srv.Handler to reply to them. func (srv *Server) Serve(l net.Listener) error { defer l.Close() var tempDelay time.Duration // how long to sleep on accept failure for { rw, e := l.Accept() if e != nil { if ne, ok := e.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay) time.Sleep(tempDelay) continue } return e } tempDelay = 0 c, err := srv.newConn(rw) if err != nil { continue } c.setState(c.rwc, StateNew) // before Serve can return go c.serve() } }
ListenAndServe()的功能:
一、初始化一個Server
二、調用Server的ListenAndServe()
三、調用net.Listen(「tcp」, addr)監聽端口
四、啓動一個for循環,在循環體中Accept請求
五、對每一個請求實例化一個Conn,而且開啓一個goroutine爲這個請求進行服務go c.serve()
下面看看進入c.serve()方法的源碼
// Serve a new connection. func (c *conn) serve() { origConn := c.rwc // copy it before it's set nil on Close or Hijack defer func() { if err := recover(); err != nil { const size = 64 << 10 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf) } if !c.hijacked() { c.close() c.setState(origConn, StateClosed) } }() if tlsConn, ok := c.rwc.(*tls.Conn); ok { if d := c.server.ReadTimeout; d != 0 { c.rwc.SetReadDeadline(time.Now().Add(d)) } if d := c.server.WriteTimeout; d != 0 { c.rwc.SetWriteDeadline(time.Now().Add(d)) } if err := tlsConn.Handshake(); err != nil { c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err) return } c.tlsState = new(tls.ConnectionState) *c.tlsState = tlsConn.ConnectionState() if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) { if fn := c.server.TLSNextProto[proto]; fn != nil { h := initNPNRequest{tlsConn, serverHandler{c.server}} fn(c.server, tlsConn, h) } return } } for { w, err := c.readRequest() if c.lr.N != c.server.initialLimitedReaderSize() { // If we read any bytes off the wire, we're active. c.setState(c.rwc, StateActive) } if err != nil { if err == errTooLarge { // Their HTTP client may or may not be // able to read this if we're // responding to them and hanging up // while they're still writing their // request. Undefined behavior. io.WriteString(c.rwc, "HTTP/1.1 413 Request Entity Too Large\r\n\r\n") c.closeWriteAndWait() break } else if err == io.EOF { break // Don't reply } else if neterr, ok := err.(net.Error); ok && neterr.Timeout() { break // Don't reply } io.WriteString(c.rwc, "HTTP/1.1 400 Bad Request\r\n\r\n") break } // Expect 100 Continue support req := w.req if req.expectsContinue() { if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 { // Wrap the Body reader with one that replies on the connection req.Body = &expectContinueReader{readCloser: req.Body, resp: w} } req.Header.Del("Expect") } else if req.Header.get("Expect") != "" { w.sendExpectationFailed() break } // HTTP cannot have multiple simultaneous active requests.[*] // Until the server replies to this request, it can't read another, // so we might as well run the handler in this goroutine. // [*] Not strictly true: HTTP pipelining. We could let them all process // in parallel even if their responses need to be serialized. serverHandler{c.server}.ServeHTTP(w, w.req) if c.hijacked() { return } w.finishRequest() if w.closeAfterReply { if w.requestBodyLimitHit { c.closeWriteAndWait() } break } c.setState(c.rwc, StateIdle) } }
上面的代碼實現:
一、讀取每一個請求的內容w, err := c.readRequest()
二、判斷handler是否爲空,若是沒有設置handler(這個例子就沒有設置handler),handler就設置爲DefaultServeMux
三、調用handler的ServeHttp
四、根據request選擇handler,而且進入到這個handler的ServeHTTP
五、選擇handler
下面就開始Http請求的過程了。上文中提到的handler在beego項目中就是beego.ControllerRegistor結構體,下面是ControllerRegistor的源碼。能夠看見。實現了http.handler接口的ServeHTTP方法。並且,上下文對象context也被初始化了。後面的do_filter就是實現過濾方法的實現。而後就是判斷請求的方法、seesion等函數。
並且還有一個很重要的就是runrouter、runMethod、findrouter、routerInfo這四個參數,在方法開頭就已經定義了。
// ControllerRegistor containers registered router rules, controller handlers and filters. type ControllerRegistor struct { routers map[string]*Tree enableFilter bool filters map[int][]*FilterRouter } // Implement http.Handler interface. func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) { defer p.recoverPanic(rw, r) starttime := time.Now() var runrouter reflect.Type var findrouter bool var runMethod string var routerInfo *controllerInfo w := &responseWriter{writer: rw} if RunMode == "dev" { w.Header().Set("Server", BeegoServerName) } // init context context := &beecontext.Context{ ResponseWriter: w, Request: r, Input: beecontext.NewInput(r), Output: beecontext.NewOutput(), } context.Output.Context = context context.Output.EnableGzip = EnableGzip // defined filter function do_filter := func(pos int) (started bool) { if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { if ok, p := filterR.ValidRouter(r.URL.Path); ok { context.Input.Params = p filterR.filterFunc(context) if w.started { return true } } } } } return false } // filter wrong httpmethod if _, ok := HTTPMETHOD[r.Method]; !ok { http.Error(w, "Method Not Allowed", 405) goto Admin } // filter for static file if do_filter(BeforeStatic) { goto Admin } serverStaticRouter(context) if w.started { findrouter = true goto Admin } // session init if SessionOn { context.Input.CruSession = GlobalSessions.SessionStart(w, r) defer func() { context.Input.CruSession.SessionRelease(w) }() } if r.Method != "GET" && r.Method != "HEAD" { if CopyRequestBody && !context.Input.IsUpload() { context.Input.CopyBody() } context.Input.ParseFormOrMulitForm(MaxMemory) } if do_filter(BeforeRouter) { goto Admin } if context.Input.RunController != nil && context.Input.RunMethod != "" { findrouter = true runMethod = context.Input.RunMethod runrouter = context.Input.RunController } if !findrouter { if t, ok := p.routers[r.Method]; ok { runObject, p := t.Match(r.URL.Path) if r, ok := runObject.(*controllerInfo); ok { routerInfo = r findrouter = true if splat, ok := p[":splat"]; ok { splatlist := strings.Split(splat, "/") for k, v := range splatlist { p[strconv.Itoa(k)] = v } } context.Input.Params = p } } } //if no matches to url, throw a not found exception if !findrouter { middleware.Exception("404", rw, r, "") goto Admin } if findrouter { //execute middleware filters if do_filter(BeforeExec) { goto Admin } isRunable := false if routerInfo != nil { if routerInfo.routerType == routerTypeRESTFul { if _, ok := routerInfo.methods[r.Method]; ok { isRunable = true routerInfo.runfunction(context) } else { middleware.Exception("405", rw, r, "Method Not Allowed") goto Admin } } else if routerInfo.routerType == routerTypeHandler { isRunable = true routerInfo.handler.ServeHTTP(rw, r) } else { runrouter = routerInfo.controllerType method := r.Method if r.Method == "POST" && context.Input.Query("_method") == "PUT" { method = "PUT" } if r.Method == "POST" && context.Input.Query("_method") == "DELETE" { method = "DELETE" } if m, ok := routerInfo.methods[method]; ok { runMethod = m } else if m, ok = routerInfo.methods["*"]; ok { runMethod = m } else { runMethod = method } } } // also defined runrouter & runMethod from filter if !isRunable { //Invoke the request handler vc := reflect.New(runrouter) execController, ok := vc.Interface().(ControllerInterface) if !ok { panic("controller is not ControllerInterface") } //call the controller init function execController.Init(context, runrouter.Name(), runMethod, vc.Interface()) //call prepare function execController.Prepare() //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf if EnableXSRF { execController.XsrfToken() if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || (r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) { execController.CheckXsrfCookie() } } execController.URLMapping() if !w.started { //exec main logic switch runMethod { case "GET": execController.Get() case "POST": execController.Post() case "DELETE": execController.Delete() case "PUT": execController.Put() case "HEAD": execController.Head() case "PATCH": execController.Patch() case "OPTIONS": execController.Options() default: if !execController.HandlerFunc(runMethod) { in := make([]reflect.Value, 0) method := vc.MethodByName(runMethod) method.Call(in) } } //render template if !w.started && context.Output.Status == 0 { if AutoRender { if err := execController.Render(); err != nil { panic(err) } } } } // finish all runrouter. release resource execController.Finish() } //execute middleware filters if do_filter(AfterExec) { goto Admin } } do_filter(FinishRouter) Admin: timeend := time.Since(starttime) //admin module record QPS if EnableAdmin { if FilterMonitorFunc(r.Method, r.URL.Path, timeend) { if runrouter != nil { go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runrouter.Name(), timeend) } else { go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, "", timeend) } } } if RunMode == "dev" { var devinfo string if findrouter { if routerInfo != nil { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s | % -40s |", r.Method, r.URL.Path, timeend.String(), "match", routerInfo.pattern) } else { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "match") } } else { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch") } Debug(devinfo) } // Call WriteHeader if status code has been set changed if context.Output.Status != 0 { w.writer.WriteHeader(context.Output.Status) } }
下面開始看看路由的調用,上面代碼中的
if findrouter { //execute middleware filters if do_filter(BeforeExec) { goto Admin } isRunable := false if routerInfo != nil { if routerInfo.routerType == routerTypeRESTFul { if _, ok := routerInfo.methods[r.Method]; ok { isRunable = true routerInfo.runfunction(context) } else { middleware.Exception("405", rw, r, "Method Not Allowed") goto Admin } } else if routerInfo.routerType == routerTypeHandler { isRunable = true routerInfo.handler.ServeHTTP(rw, r) } else { runrouter = routerInfo.controllerType method := r.Method if r.Method == "POST" && context.Input.Query("_method") == "PUT" { method = "PUT" } if r.Method == "POST" && context.Input.Query("_method") == "DELETE" { method = "DELETE" } if m, ok := routerInfo.methods[method]; ok { runMethod = m } else if m, ok = routerInfo.methods["*"]; ok { runMethod = m } else { runMethod = method } } }
首先是do_filter(BeforeExec)進入go Admin,執行控制器方法前面的過濾。而後vc := reflect.New(runrouter)建立一個控制器實例。最後在執行do_filter(AfterExec)過濾器方法。在execController.Init(context, runrouter.Name(), runMethod, vc.Interface())裏面實現了初始化controller的方法。每次請求都會不相同。
最後在下面的代碼
if !w.started { //exec main logic switch runMethod { case "GET": execController.Get() case "POST": execController.Post() case "DELETE": execController.Delete() case "PUT": execController.Put() case "HEAD": execController.Head() case "PATCH": execController.Patch() case "OPTIONS": execController.Options() default: if !execController.HandlerFunc(runMethod) { in := make([]reflect.Value, 0) method := vc.MethodByName(runMethod) method.Call(in) } }
按照不一樣的求情方式請求不一樣的方法。默認的是根據反射。而後method.call()來調用。實現了router的路由註冊。。
終於寫完了。其實我也不知道本身寫的是什麼。過幾天再改進。。待續。。。。