一:首先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[] != "" {
strs := strings.Split(params[], ":")
if len(strs) > 0 && strs[] != "" {
HttpAddr = strs[]
}
if len(strs) > 1 && strs[] != "" {
HttpPort, _ = strconv.Atoi(strs[])
}
}
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("", 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("", 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("", 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的路由注册。。
终于写完了。其实我也不知道自己写的是什么。过几天再改进。。待续。。。。