了解-路由及controller的处理过程
在初探和起源中我们知道了基本原理、初始化及启动时的处理,本节主要探究controller的大致处理过程。
一、系统路由及controller func信息的存储大致过程
在初探里我们列出了各种路由的调用过程,最终均会调用p.AddToRouter。因此我们先根据其中特殊的几个了解下详细的过程。
1.Get-基础路由
beego.Get->BeeApp.Handlers.Get->p.AddMethod->p.addToRouter
我们先了解下AddMethod,具体注释在行尾:
func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) {
method = strings.ToUpper(method)
if _, ok := HTTPMETHOD[method]; method != "*" && !ok {//剔除不支持的请求方式
panic("not support http method: " + method)
}
route := &ControllerInfo{}
route.pattern = pattern//设置路由
route.routerType = routerTypeRESTFul//restful router类型
route.runFunction = f//设置处理方法
methods := make(map[string]string)
if method == "*" {//如果是全匹配,添加所有请求方式
for _, val := range HTTPMETHOD {
methods[val] = val
}
} else {//否则只添加指定请求方式
methods[method] = method
}
route.methods = methods
for k := range methods {//遍历请求方式
if k == "*" {//如果全匹配,遍历添加所有请求方式m、pattern及route到Tree上
for _, m := range HTTPMETHOD {
p.addToRouter(m, pattern, route)
}
} else {//否则只添加指定请求方式k、pattern及route到Tree上
p.addToRouter(k, pattern, route)
}
}
}
2.Router-固定/正则/自定义方法路由
beego.Router->BeeApp.Handlers.Add->p.addWithMethodParams->p.addToRouter
我们先了解下addWithMethodParams,具体注释在行尾:
//调用到此处时methodParams为nil
func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, mappingMethods ...string) {
reflectVal := reflect.ValueOf(c)
t := reflect.Indirect(reflectVal).Type()//获取controller指向值的类型,后期匹配后会反射获取其对象
methods := make(map[string]string)//方法集
if len(mappingMethods) > 0 {//指定处理方法
semi := strings.Split(mappingMethods[0], ";")//是否包含多种请求方式及处理方法
for _, v := range semi {
colon := strings.Split(v, ":")//拆分请求方式及处理方法
if len(colon) != 2 {
panic("method mapping format is invalid")
}
comma := strings.Split(colon[0], ",")//拆分请求方式
for _, m := range comma {
if _, ok := HTTPMETHOD[strings.ToUpper(m)]; m == "*" || ok {//合法的请求方式或通配符
if val := reflectVal.MethodByName(colon[1]); val.IsValid() {//反射获取指定controller内的方法是否存在
methods[strings.ToUpper(m)] = colon[1]//根据请求方式保存处理方法
} else {
panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name())
}
} else {
panic(v + " is an invalid method mapping. Method doesn't exist " + m)
}
}
}
}
route := &ControllerInfo{}
route.pattern = pattern//设置路由
route.methods = methods//设置方法集
route.routerType = routerTypeBeego//默认beego类型router
route.controllerType = t//设置controller的类型
route.methodParams = methodParams//nil
if len(methods) == 0 {//未指定方法时
for _, m := range HTTPMETHOD {
p.addToRouter(m, pattern, route)
}
} else {
for k := range methods {//指定方法时
if k == "*" {//通配符,需要添加所有的请求方式到router
for _, m := range HTTPMETHOD {
p.addToRouter(m, pattern, route)
}
} else {//添加指定的请求方式到router
p.addToRouter(k, pattern, route)
}
}
}
}
以上代码提到Router的用法有两种,①指定单个处理的controller,②指定多处理的controller的某个方法
3.AutoRouter-自动路由
beego.AutoRouter->BeeApp.Handlers.AddAuto->p.AddAutoPrefix->p.addToRouter
我们先了解下AddAutoPrefix,具体注释在行尾:
//调用到此处时prefix为"/"
func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) {
reflectVal := reflect.ValueOf(c)
rt := reflectVal.Type()//获取类型
ct := reflect.Indirect(reflectVal).Type()//获取controller指向值的类型,后期匹配后会反射获取其对象
controllerName := strings.TrimSuffix(ct.Name(), "Controller")//获取controller的名称前缀
for i := 0; i < rt.NumMethod(); i++ {//遍历可导出方法集
if !utils.InSlice(rt.Method(i).Name, exceptMethod) {//排除beego自有方法
route := &ControllerInfo{}
route.routerType = routerTypeBeego//默认beego类型router
route.methods = map[string]string{"*": rt.Method(i).Name}//保存方法集,默认适合所有请求方式
route.controllerType = ct//设置controller的类型
pattern := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name), "*")//小写格式pattern并带通配符(/controllername/method/*)
patternInit := path.Join(prefix, controllerName, rt.Method(i).Name, "*")//原有格式pattern并带通配符(/controllerName/Method/*)
patternFix := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name))小写格式pattern(/controllername/method)
patternFixInit := path.Join(prefix, controllerName, rt.Method(i).Name)//原有格式pattern(/controllerName/Method)
route.pattern = pattern//默认pattern
for _, m := range HTTPMETHOD {//添加所有请求方式到router中
p.addToRouter(m, pattern, route)
p.addToRouter(m, patternInit, route)
p.addToRouter(m, patternFix, route)
p.addToRouter(m, patternFixInit, route)
}
}
}
}
4.Include-注解路由
beego.Include->BeeApp.Handlers.Include->p.addWithMethodParams->p.addToRouter
也调用到了p.addWithMethodParams,具体分析见指定路由1
鉴于这个是注解路由我们还是有必要了解下:
func (p *ControllerRegister) Include(cList ...ControllerInterface) {
if BConfig.RunMode == DEV {//dev模式
skip := make(map[string]bool, 10)
for _, c := range cList {//遍历controller
reflectVal := reflect.ValueOf(c)
t := reflect.Indirect(reflectVal).Type()//获取controller指向值的类型,后期匹配后会反射获取其对象
wgopath := utils.GetGOPATHs()//获取gopath
if len(wgopath) == 0 {
panic("you are in dev mode. So please set gopath")
}
pkgpath := ""
for _, wg := range wgopath {
wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", t.PkgPath()))//包名完整路径
if utils.FileExists(wg) {//判断文件是否存在
pkgpath = wg
break
}
}
if pkgpath != "" {
if _, ok := skip[pkgpath]; !ok {//是否处理过
skip[pkgpath] = true
parserPkg(pkgpath, t.PkgPath())//生成对应的controller文件
}
}
}
}
for _, c := range cList {//遍历controller
reflectVal := reflect.ValueOf(c)
t := reflect.Indirect(reflectVal).Type()
key := t.PkgPath() + ":" + t.Name()//拼凑controller路径,如test/controllers:TestController
if comm, ok := GlobalControllerRouter[key]; ok {//从GlobalControllerRouter中获取
for _, a := range comm {//遍历所有路由、控制器、参数、请求方式及处理方法
p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)
}
}
}
}
从以上几个路由的处理来看,我们知道在调用p.AddToRouter前,methods是按照请求方式、处理方法对应关系保存到route中。
5.Handler-自定义Handler
beego.Handler->BeeApp.Handlers.Handler->p.addToRouter
func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) {
route := &ControllerInfo{}
route.pattern = pattern//设置路由
route.routerType = routerTypeHandler//handler router累心
route.handler = h//设置处理handler
if len(options) > 0 {
if _, ok := options[0].(bool); ok {//是否前缀匹配
pattern = path.Join(pattern, "?:all(.*)")
}
}
for _, m := range HTTPMETHOD {//遍历添加支持所有请求方式
p.addToRouter(m, pattern, route)
}
}
6.addToRouter
// method 请求方式
// pattern 路由
// r route信息(包含类型、方法集等信息)
func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) {
if !BConfig.RouterCaseSensitive {
pattern = strings.ToLower(pattern)
}
if t, ok := p.routers[method]; ok {//当前请求方式Tree是否存在
t.AddRouter(pattern, r)//存在直接添加
} else {
t := NewTree()//不存在新建Tree再添加
t.AddRouter(pattern, r)
p.routers[method] = t//最后保存至routers中
}
}
至此,所有的路由信息均保存至p.routers中,即BeeApp.Handler.routers中。关于路由t.AddRouter的具体细节我们稍后的章节再来研究。
二、请求路由与controller的处理大致过程
我们在初探中提过,http请求最终会交由*ControllerRegister的ServeHTTP来处理,ServeHTTP是一个很长很长的func,我们在这里只关注重点的部分,同样关于匹配的细节此处不深究。
func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
...
// User can define RunController and RunMethod in filter
if context.Input.RunController != nil && context.Input.RunMethod != "" {
findRouter = true
runMethod = context.Input.RunMethod
runRouter = context.Input.RunController
} else {
routerInfo, findRouter = p.FindRouter(context)//根据请求路由查找routerInfo,此routerInfo即是我们在第一步存入的route
}
...
if routerInfo != nil {//如果查找到routerInfo
//store router pattern into context
context.Input.SetData("RouterPattern", routerInfo.pattern)
//根据routerType处理
if routerInfo.routerType == routerTypeRESTFul {//restful类型如beego.Get
if _, ok := routerInfo.methods[r.Method]; ok {//匹配请求方式,匹配后直接交由runFunction处理
isRunnable = true
routerInfo.runFunction(context)
} else {
exception("405", context)
goto Admin
}
} else if routerInfo.routerType == routerTypeHandler {//自定义Handler类型,如beego.Handler
isRunnable = true
routerInfo.handler.ServeHTTP(rw, r)//直接交由handler处理
} else {//其他类型,主要是routerTypeBeego,如beego.Router、beego.AutoRouter、beego.Include、beego.AddNamespace等
runRouter = routerInfo.controllerType//获取controller类型
methodParams = routerInfo.methodParams//获取方法参数
method := r.Method//获取请求方式
if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPost {
method = http.MethodPut
}
if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodDelete {
method = http.MethodDelete
}
//获取存储在map中请求方式对应的方法名
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 !isRunnable {
//Invoke the request handler
vc := reflect.New(runRouter)//反射获取controller对象
execController, ok := vc.Interface().(ControllerInterface)//转为ControllerInterface类型,以方便调用以下接口
if !ok {
panic("controller is not ControllerInterface")
}
...
if !context.ResponseWriter.Started {//未响应时,正常我们的controller命名的func名称不会与请求方式名称一样,如果一样的话就会交由下面报错处理
//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) {
method := vc.MethodByName(runMethod)//从controller对象中根据方法名反射获取方法
in := param.ConvertParams(methodParams, method.Type(), context)
out := method.Call(in)//调用方法
//For backward compatibility we only handle response if we had incoming methodParams
if methodParams != nil {
p.handleParamResponse(context, execController, out)
}
}
}
...
// finish all runRouter. release resource
execController.Finish()
}
...
}