github.com/zhongdalu/gf@v1.0.0/g/net/ghttp/ghttp_server_service_controller.go (about) 1 // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 // 服务注册. 7 8 package ghttp 9 10 import ( 11 "fmt" 12 "reflect" 13 "strings" 14 15 "github.com/zhongdalu/gf/g/os/gfile" 16 "github.com/zhongdalu/gf/g/os/glog" 17 "github.com/zhongdalu/gf/g/text/gregex" 18 "github.com/zhongdalu/gf/g/text/gstr" 19 ) 20 21 // 绑定控制器,控制器需要实现 gmvc.Controller 接口, 22 // 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话, 23 // 第三个参数methods用以指定需要注册的方法,支持多个方法名称,多个方法以英文“,”号分隔,区分大小写. 24 func (s *Server) BindController(pattern string, c Controller, methods ...string) { 25 // 当pattern中的method为all时,去掉该method,以便于后续方法判断 26 domain, method, path, err := s.parsePattern(pattern) 27 if err != nil { 28 glog.Error(err) 29 return 30 } 31 if strings.EqualFold(method, gDEFAULT_METHOD) { 32 pattern = s.serveHandlerKey("", path, domain) 33 } 34 35 methodMap := (map[string]bool)(nil) 36 if len(methods) > 0 { 37 methodMap = make(map[string]bool) 38 for _, v := range strings.Split(methods[0], ",") { 39 methodMap[strings.TrimSpace(v)] = true 40 } 41 } 42 // 遍历控制器,获取方法列表,并构造成uri 43 m := make(handlerMap) 44 v := reflect.ValueOf(c) 45 t := v.Type() 46 sname := t.Elem().Name() 47 pkgPath := t.Elem().PkgPath() 48 pkgName := gfile.Basename(pkgPath) 49 for i := 0; i < v.NumMethod(); i++ { 50 mname := t.Method(i).Name 51 if methodMap != nil && !methodMap[mname] { 52 continue 53 } 54 if mname == "Init" || mname == "Shut" || mname == "Exit" { 55 continue 56 } 57 ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") 58 if ctlName[0] == '*' { 59 ctlName = fmt.Sprintf(`(%s)`, ctlName) 60 } 61 if _, ok := v.Method(i).Interface().(func()); !ok { 62 if len(methodMap) > 0 { 63 // 指定的方法名称注册,那么需要使用错误提示 64 glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`, 65 pkgPath, ctlName, mname, v.Method(i).Type().String()) 66 } else { 67 // 否则只是Debug提示 68 glog.Debugf(`ignore route method: %s.%s.%s defined as "%s", no match "func()"`, 69 pkgPath, ctlName, mname, v.Method(i).Type().String()) 70 } 71 continue 72 } 73 key := s.mergeBuildInNameToPattern(pattern, sname, mname, true) 74 m[key] = &handlerItem{ 75 name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname), 76 rtype: gROUTE_REGISTER_CONTROLLER, 77 ctype: v.Elem().Type(), 78 fname: mname, 79 faddr: nil, 80 } 81 // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI, 82 // 例如: pattern为/user, 那么会同时注册/user及/user/index, 83 // 这里处理新增/user路由绑定。 84 // 注意,当pattern带有内置变量时,不会自动加该路由。 85 if strings.EqualFold(mname, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) { 86 p := gstr.PosR(key, "/index") 87 k := key[0:p] + key[p+6:] 88 if len(k) == 0 || k[0] == '@' { 89 k = "/" + k 90 } 91 m[k] = &handlerItem{ 92 name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname), 93 rtype: gROUTE_REGISTER_CONTROLLER, 94 ctype: v.Elem().Type(), 95 fname: mname, 96 faddr: nil, 97 } 98 } 99 } 100 s.bindHandlerByMap(m) 101 } 102 103 // 绑定路由到指定的方法执行, 第三个参数method仅支持一个方法注册,不支持多个,并且区分大小写。 104 func (s *Server) BindControllerMethod(pattern string, c Controller, method string) { 105 m := make(handlerMap) 106 v := reflect.ValueOf(c) 107 t := v.Type() 108 sname := t.Elem().Name() 109 mname := strings.TrimSpace(method) 110 fval := v.MethodByName(mname) 111 if !fval.IsValid() { 112 glog.Error("invalid method name:" + mname) 113 return 114 } 115 pkgPath := t.Elem().PkgPath() 116 pkgName := gfile.Basename(pkgPath) 117 ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") 118 if ctlName[0] == '*' { 119 ctlName = fmt.Sprintf(`(%s)`, ctlName) 120 } 121 if _, ok := fval.Interface().(func()); !ok { 122 glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`, 123 pkgPath, ctlName, mname, fval.Type().String()) 124 return 125 } 126 key := s.mergeBuildInNameToPattern(pattern, sname, mname, false) 127 m[key] = &handlerItem{ 128 name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname), 129 rtype: gROUTE_REGISTER_CONTROLLER, 130 ctype: v.Elem().Type(), 131 fname: mname, 132 faddr: nil, 133 } 134 s.bindHandlerByMap(m) 135 } 136 137 // 绑定控制器(RESTFul),控制器需要实现gmvc.Controller接口 138 // 方法会识别HTTP方法,并做REST绑定处理,例如:Post方法会绑定到HTTP POST的方法请求处理,Delete方法会绑定到HTTP DELETE的方法请求处理 139 // 因此只会绑定HTTP Method对应的方法,其他方法不会自动注册绑定 140 // 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话 141 func (s *Server) BindControllerRest(pattern string, c Controller) { 142 // 遍历控制器,获取方法列表,并构造成uri 143 m := make(handlerMap) 144 v := reflect.ValueOf(c) 145 t := v.Type() 146 sname := t.Elem().Name() 147 pkgPath := t.Elem().PkgPath() 148 // 如果存在与HttpMethod对应名字的方法,那么绑定这些方法 149 for i := 0; i < v.NumMethod(); i++ { 150 mname := t.Method(i).Name 151 method := strings.ToUpper(mname) 152 if _, ok := methodsMap[method]; !ok { 153 continue 154 } 155 pkgName := gfile.Basename(pkgPath) 156 ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") 157 if ctlName[0] == '*' { 158 ctlName = fmt.Sprintf(`(%s)`, ctlName) 159 } 160 if _, ok := v.Method(i).Interface().(func()); !ok { 161 glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`, 162 pkgPath, ctlName, mname, v.Method(i).Type().String()) 163 return 164 } 165 key := s.mergeBuildInNameToPattern(mname+":"+pattern, sname, mname, false) 166 m[key] = &handlerItem{ 167 name: fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname), 168 rtype: gROUTE_REGISTER_CONTROLLER, 169 ctype: v.Elem().Type(), 170 fname: mname, 171 faddr: nil, 172 } 173 } 174 s.bindHandlerByMap(m) 175 }