• beego项目运行过程


    一:首先man.go,整个程序的入口

    func main() {
        beego.Run()
    }

    然后beego.run()代码

    // 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()可以带参数。

    beego.run()在默认的主机、端口号上运行,beego.run(port)在给定的端口号、默认的主机上运行。beego.run(addr:post)在给定的主机和端口上运行。

    下面看看initBeforeHttpRun()的代码。

    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。

    然后是

     for _, hk := range hooks {
            err := hk()
            if err != nil {
                panic(err)
            }
        }

    hooks的定义

    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的切片,

    // 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是在启动之前,留给用户初始化一些东西的时候。

    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()的功能:

    1、初始化一个Server

    2、调用Server的ListenAndServe()

    3、调用net.Listen(“tcp”, addr)监听端口

    4、启动一个for循环,在循环体中Accept请求

    5、对每个请求实例化一个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)
        }
    }

    上面的代码实现:

    1、读取每个请求的内容w, err := c.readRequest()

    2、判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux

    3、调用handler的ServeHttp

    4、根据request选择handler,并且进入到这个handler的ServeHTTP

    5、选择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的路由注册。。

    终于写完了。其实我也不知道自己写的是什么。过几天再改进。。待续。。。。

  • 相关阅读:
    C#下水晶报表打印自定义纸张
    设计模式应用之一:控件清空
    自定义StyleCop规则
    1.redis安装 单机
    dubbo个人总结
    spring+redis 集群下的操作
    mybatis调用存储过程 无参、带有输入输出参数,输出游标类型的 存储
    spring framework核心框架体系结构
    oracle‘s package,function,proceture编译时无响应(解决)
    Oracle 在线重定义表分区
  • 原文地址:https://www.cnblogs.com/guhao123/p/4056978.html
Copyright © 2020-2023  润新知