beego項目運行過程

一:首先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的路由註冊。。

終於寫完了。其實我也不知道本身寫的是什麼。過幾天再改進。。待續。。。。

相關文章
相關標籤/搜索