// vendor/github.com/astaxie/beego/controller.go // Controller defines some basic http request handler operations, such as // http context, template and view, session and xsrf. type Controller struct { // http ctx数据,beego的Context基于http.Context做了一层封装 Ctx *context.Context Data map[interface{}]interface{}
// template data // view相关的信息 TplName string ViewPath string Layout string LayoutSections map[string]string// the key is the section name and the value is the template name TplPrefix string TplExt string EnableRender bool
// xsrf data _xsrfToken string XSRFExpire int EnableXSRF bool
// App defines beego application with a new PatternServeMux. type App struct { Handlers *ControllerRegister //自定义Handler,实现了http.Handler接口,通过反射来实现不同路由调用对应的处理函数 Server *http.Server // 实际的http server }
numOfFields := elemVal.NumField() for i := 0; i < numOfFields; i++ { fieldType := elemType.Field(i) elemField := execElem.FieldByName(fieldType.Name) if elemField.CanSet() { fieldVal := elemVal.Field(i) elemField.Set(fieldVal) } }
return execController }
route.methodParams = methodParams iflen(methods) == 0 { // 对应于("/user",&UserController{})的场景,methods没有指定 for m := range HTTPMETHOD { p.addToRouter(m, pattern, route) } } else { // 对应于指定了method的场景 for k := range methods { if k == "*" { for m := range HTTPMETHOD { p.addToRouter(m, pattern, route) } } else { p.addToRouter(k, pattern, route) } } } }
// src/net/http/server.go // A Handler responds to an HTTP request. // // ServeHTTP should write reply headers and data to the ResponseWriter // and then return. Returning signals that the request is finished; it // is not valid to use the ResponseWriter or read from the // Request.Body after or concurrently with the completion of the // ServeHTTP call. // // Depending on the HTTP client software, HTTP protocol version, and // any intermediaries between the client and the Go server, it may not // be possible to read from the Request.Body after writing to the // ResponseWriter. Cautious handlers should read the Request.Body // first, and then reply. // // Except for reading the body, handlers should not modify the // provided Request. // // If ServeHTTP panics, the server (the caller of ServeHTTP) assumes // that the effect of the panic was isolated to the active request. // It recovers the panic, logs a stack trace to the server error log, // and either closes the network connection or sends an HTTP/2 // RST_STREAM, depending on the HTTP protocol. To abort a handler so // the client sees an interrupted response but the server doesn't log // an error, panic with the value ErrAbortHandler. type Handler interface { ServeHTTP(ResponseWriter, *Request) }
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 init if BConfig.WebConfig.Session.SessionOn { var err error context.Input.CruSession, err = GlobalSessions.SessionStart(rw, r) if err != nil { logs.Error(err) exception("503", context) goto Admin } deferfunc() { if context.Input.CruSession != nil { context.Input.CruSession.SessionRelease(rw) } }() } iflen(p.filters[BeforeRouter]) > 0 && p.execFilter(context, urlPath, BeforeRouter) { goto Admin } // 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 { // 关键逻辑,找到改请求对应的ControllerInfo,在这个过程中也会把":id"、":splat"等值写入到ctx的params中 routerInfo, findRouter = p.FindRouter(context) }
// if no matches to url, throw a not found exception if !findRouter { exception("404", context) goto Admin } if splat := context.Input.Param(":splat"); splat != "" { for k, v := range strings.Split(splat, "/") { context.Input.SetParam(strconv.Itoa(k), v) } }
if routerInfo != nil { // store router pattern into context context.Input.SetData("RouterPattern", routerInfo.pattern) }
// check policies if p.execPolicy(context, urlPath) { goto Admin }
if routerInfo != nil { if routerInfo.routerType == routerTypeRESTFul { // routerTypeRESTFul:AddMethod()的方式注册的路由 if _, ok := routerInfo.methods[r.Method]; ok { isRunnable = true routerInfo.runFunction(context) } else { exception("405", context) goto Admin } } elseif routerInfo.routerType == routerTypeHandler { // routerTypeHandler:Handler()的方式注册的路由 isRunnable = true routerInfo.handler.ServeHTTP(context.ResponseWriter, context.Request) } else { // routerTypeBeego:Add()或者AddAuto()的方式注册的路由,前者就是2.1节中的方式 runRouter = routerInfo.controllerType methodParams = routerInfo.methodParams method := r.Method if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPut { 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 } elseif m, ok = routerInfo.methods["*"]; ok { runMethod = m } else { runMethod = method } } }
// also defined runRouter & runMethod from filter if !isRunnable { // Invoke the request handler var execController ControllerInterface if routerInfo != nil && routerInfo.initialize != nil { execController = routerInfo.initialize() } else { // 关键逻辑:实例化一个Controller,如1.2样例中的SampleController vc := reflect.New(runRouter) var ok bool execController, ok = vc.Interface().(ControllerInterface) if !ok { panic("controller is not ControllerInterface") } }
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() case http.MethodTrace: execController.Trace() default: if !execController.HandlerFunc(runMethod) { vc := reflect.ValueOf(execController) // 根据之前注册的Controller的函数名找到对应的方法 method := vc.MethodByName(runMethod) in := param.ConvertParams(methodParams, method.Type(), context) // 关键逻辑,调用注册的方法,如1.2中的Hello() out := method.Call(in)
// For backward compatibility we only handle response if we had incoming methodParams 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) } } } }