Beego源码解析Router

Beego源码解析之Router


初始化控制器
func NewControllerRegister() *ControllerRegister {
	cr := &ControllerRegister{
		routers:  make(map[string]*Tree),
		policies: make(map[string]*Tree),
	}
	cr.pool.New = func() interface{} {
		return beecontext.NewContext()
	}
	return cr
}

ServeHttp(rw http.ResponseWriter, r *http.Request)

ServeHttp实现/Go/src/net/http/server.go中Handler接口,一个handler表示一个http request,所以调用一次ServerHttp表示一个请求。ServerHttp需将回复的headers和data写回ResponseWriter,然后回传(FIX)。

context := p.pool.Get().(*beecontext.Context) //从 ControllerRegister中的pool对象池获得一个 Context对象 
context.Reset(rw, r) // reset为重新初始化context里的对象,详见beego_code_context.md

参数配置

context.Output.EnableGzip = BConfig.EnableGzip

if BConfig.RunMode == DEV {
	context.Output.Header("Server", BConfig.ServerName)
}

if !BConfig.RouterCaseSensitive {
	urlPath = strings.ToLower(urlPath)
}

寻找静态路径前,会根据过滤函数,对url进行验证,判断是否符合请求规范

// filter for static file
if len(p.filters[BeforeStatic]) > 0 && p.execFilter(context, urlPath, BeforeStatic) {
	goto Admin
}

请求服务器静态文件,如果找到请求对应的静态文件写入ResponseWriter,并把ResponseWriter.Started设为true,该方法只对Method为GET和HEAD适用

serverStaticRouter(context)

if context.ResponseWriter.Started {
	findRouter = true
	goto Admin
}
if r.Method != http.MethodGet && r.Method != http.MethodHead {
	if BConfig.CopyRequestBody && !context.Input.IsUpload() {
		context.Input.CopyBody(BConfig.MaxMemory)
	}
	context.Input.ParseFormOrMulitForm(BConfig.MaxMemory)
}

然后初始化session(如果beego config有开session),寻找路由之前进行路由过滤。

如果Input.RunController不为空且Input.RunMethod不为空,则设定findRouter = true,没找到的话通过FindRouter找request的url对应的路由是match哪个控制器的方法,如果通过路由树都没找到就回传404。

找到路由后,routerInfo的机构体。routerInfo是一个控制器的结构体,记录这个如有的方法(???为什么是数组而不是一个)及对应控制器的相关属性。

type ControllerInfo struct {
	pattern        string
	controllerType reflect.Type
	methods        map[string]string
	handler        http.Handler
	runFunction    FilterFunc
	routerType     int
	initialize     func() ControllerInterface
	methodParams   []*param.MethodParam
}
if context.Input.RunController != nil && context.Input.RunMethod != "" {
	findRouter = true
	runMethod = context.Input.RunMethod
	runRouter = context.Input.RunController
} else {
	routerInfo, findRouter = p.FindRouter(context)
}

if !findRouter {
	exception("404", context)
	goto Admin
}

找到路由之后,绑定路径参数到context.Input内

if splat := context.Input.Param(":splat"); splat != "" {
	for k, v := range strings.Split(splat, "/") {
		context.Input.SetParam(strconv.Itoa(k), v)
	}
}

执行之前进行中间件过滤及查看policy

if len(p.filters[BeforeExec]) > 0 && p.execFilter(context, urlPath, BeforeExec) {
	goto Admin
}
if p.execPolicy(context, urlPath) {
	goto Admin
}

找到路由的判断方式是,上面的判断上面routerInfo是否为nil,如果不为nil则通过判断routerInfo.routerType来决定要怎么往下执行。有三种routerType:

routerTypeBeego: 0
routerTypeRESTFul:1
routerTypeHandler: 2

如果是routerTypeRESTFul,判断request的method是否和请求routerInfo这个方法一致,如果一致则直接执行routerInfo的runFunction。否则回传405,Method Not Allowed;

如果是routerTypeHandler,则执行context的handler里的ServeHTTP function;

如果上述两种皆非,会通过routerInfo里的控制器结构,得到请求对应的控制器对象信息,请求方法及请求参数;

if routerInfo != nil {
		context.Input.SetData("RouterPattern", routerInfo.pattern)
		if routerInfo.routerType == routerTypeRESTFul {
			if _, ok := routerInfo.methods[r.Method]; ok {
				isRunnable = true
				routerInfo.runFunction(context)
			} else {
				exception("405", context)
				goto Admin
			}
		} else if routerInfo.routerType == routerTypeHandler {
			isRunnable = true
			routerInfo.handler.ServeHTTP(rw, r)
		} else {
			runRouter = routerInfo.controllerType
			methodParams = routerInfo.methodParams
			method := r.Method
			if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPost { //旧版的判断式http.MethodPut而不是http.MethodPost
				method = http.MethodPut
			}
			if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodDelete {
				method = http.MethodDelete
			}
			if m, ok := routerInfo.methods[method]; ok {
				runMethod = m
			} else if m, ok = routerInfo.methods["*"]; ok {
				runMethod = m
			} else {
				runMethod = method
			}
		}
	}

最后一个else里并没有正常执行控制器里的对应runFunction,所以在接下来的if(!isRunable)里会去尝试匹配request对应的控制器的方法。

首先会对找到的控制器对象初始化,调控制器对象的initialize function。如果没有对应的initialize function,则通过反射获得当初注册路由时对应的控制器对象。调取函数初始化需要被调用的控制器对象。

如果控制器对象有实现URLMapping function,则在调用URLMapping时执行request要调用的function。如果没有实现(beego默认该function没有做事情),表明context.ResponseWriter.Started = false,则需switch匹配request method对应的function。

如果没有在上一步没有执行对应的function,并且context.Output.Started为false,会去看该function是否是需要回传模版文件,即执行execController.render()。

最后执行execController.Finish(),执行完调用的function后才能执行Finish function。

if !isRunnable {
		var execController ControllerInterface
		if routerInfo.initialize != nil {
			execController = routerInfo.initialize()
		} else {
			vc := reflect.New(runRouter)
			var ok bool
			execController, ok = vc.Interface().(ControllerInterface)
			if !ok {
				panic("controller is not ControllerInterface")
			}
		}
    
		execController.Init(context, runRouter.Name(), runMethod, execController)

		execController.Prepare()

		// 是否开启Cross-site request forgery
		if BConfig.WebConfig.EnableXSRF {
			execController.XSRFToken()
			if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut ||
				(r.Method == http.MethodPost && (context.Input.Query("_method") == http.MethodDelete || context.Input.Query("_method") == http.MethodPut)) {
				execController.CheckXSRFCookie()
			}
		}

		execController.URLMapping()

		if !context.ResponseWriter.Started {
			//exec main logic
			switch runMethod {
			case http.MethodGet:
				execController.Get()
			case http.MethodPost:
				execController.Post()
			case http.MethodDelete:
				execController.Delete()
			case http.MethodPut:
				execController.Put()
			case http.MethodHead:
				execController.Head()
			case http.MethodPatch:
				execController.Patch()
			case http.MethodOptions:
				execController.Options()
			default:
				if !execController.HandlerFunc(runMethod) {
					vc := reflect.ValueOf(execController)
					method := vc.MethodByName(runMethod)
					in := param.ConvertParams(methodParams, method.Type(), context)
					out := method.Call(in)

					if methodParams != nil {
						p.handleParamResponse(context, execController, out)
					}
				}
			}

			//render template
			if !context.ResponseWriter.Started && context.Output.Status == 0 {
				if BConfig.WebConfig.AutoRender {
					if err := execController.Render(); err != nil {
						logs.Error(err)
					}
				}
			}
		}

		// finish all runRouter. release resource
		execController.Finish()
	}

记录请求信息

Admin:
//admin module record QPS

	statusCode := context.ResponseWriter.Status
	if statusCode == 0 {
		statusCode = 200
	}

	logAccess(context, &startTime, statusCode)

	if BConfig.Listen.EnableAdmin {
		timeDur := time.Since(startTime)
		pattern := ""
		if routerInfo != nil {
			pattern = routerInfo.pattern
		}

		if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) {
			if runRouter != nil {
				go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur)
			} else {
				go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, "", timeDur)
			}
		}
	}

	if BConfig.RunMode == DEV && !BConfig.Log.AccessLogs {
		var devInfo string
		timeDur := time.Since(startTime)
		iswin := (runtime.GOOS == "windows")
		statusColor := logs.ColorByStatus(iswin, statusCode)
		methodColor := logs.ColorByMethod(iswin, r.Method)
		resetColor := logs.ColorByMethod(iswin, "")
		if findRouter {
			if routerInfo != nil {
				devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s   r:%s", context.Input.IP(), statusColor, statusCode,
					resetColor, timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path,
					routerInfo.pattern)
			} else {
				devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
					timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path)
			}
		} else {
			devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
				timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path)
		}
		if iswin {
			logs.W32Debug(devInfo)
		} else {
			logs.Debug(devInfo)
		}
	}
// Call WriteHeader if status code has been set changed
if context.Output.Status != 0 {
	context.ResponseWriter.WriteHeader(context.Output.Status)
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值